[
  {
    "path": ".editorconfig",
    "content": "root = true\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nend_of_line = lf\n"
  },
  {
    "path": ".gitee/ISSUE_TEMPLATE.zh-CN.md",
    "content": "### 当前使用版本(必填,否则不予处理)\n\n\n\n### 该问题是如何引起的？(确定最新版也有问题再提!!!)\n\n\n\n### 重现步骤(如果有就写完整)\n\n\n\n### 报错信息\n\n\n"
  },
  {
    "path": ".gitee/PULL_REQUEST_TEMPLATE.zh-CN.md",
    "content": "### 该Pull Request关联的Issue\n\n\n\n### 修改描述\n\n\n\n### 测试用例\n\n\n\n### 修复效果的截屏\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: 错误报告\ndescription: File a bug report.\ntitle: \"[错误报告]: 描述\"\nlabels: [\"\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        请确认以下信息：\n        1. 请按此模板提交issues, 不按模板提交的问题将直接关闭\n        2. 如果你使用的版本号不是最新版, 那么你的 issue 大概率将会被直接关闭\n        3. 如果你的问题与该仓库无关或者可以直接在以往 issue 中找到, 那么你的 issue 大概率将会被直接关闭\n        4. 提交问题务必描述清楚、附上日志, 描述不清导致无法理解和分析的问题 大概率会被直接关闭\n  - type: checkboxes\n    id: confirm\n    attributes:\n      label: 确认\n      description: 在提交 issue 之前, 请确认你已经阅读并确认以下内容, 如果 不全部勾选 或 勾选与事实不符 那么你的 issue 大概率将会被直接关闭\n      options:\n        - label: 我使用的版本是[最新版](https://central.sonatype.com/search?q=g:com.baomidou%20%20a:mybatis-plus&smo=true), 并且使用插件确认过项目里无依赖版本冲突\n          required: true\n        - label: 我已经在 [issue](https://github.com/baomidou/mybatis-plus/issues) 中搜索过, 确认问题没有被提出过\n          required: true\n        - label: 我已经修改标题, 将标题中的 **描述** 替换为遇到的问题(不得删除 **描述** 前面的部分)\n          required: true\n  - type: input\n    id: version\n    attributes:\n      label: 当前程序版本\n      description: 遇到问题时程序所在的版本号\n    validations:\n      required: true\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: 问题描述\n      description: 请详细描述你碰到的问题\n      placeholder: \"问题描述\"\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      label: 详细堆栈日志\n      description: 问题出现时，程序错误堆栈日志。\n      render: bash\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: 官网\n    url: https://baomidou.com/\n    about: document.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: 功能改进\ndescription: Feature Request\ntitle: \"[功能改进]: 描述\"\nlabels: [\"\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        请说明你希望添加的功能。\n  - type: checkboxes\n    id: confirm\n    attributes:\n      label: 确认\n      description: 在提交 issue 之前，请确认你已经阅读并确认以下内容\n      options:\n        - label: 我的版本是最新版本, 我的版本号与 [version](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.baomidou%22%20AND%20a%3A%22mybatis-plus%22) 相同, 并且项目里无依赖冲突\n          required: true\n        - label: 我已经在 [issue](https://github.com/baomidou/mybatis-plus/issues) 中搜索过, 确认问题没有被提出过\n          required: true\n        - label: 我已经修改标题, 将标题中的 **描述** 替换为你的想法(不得删除 **描述** 前面的部分)\n          required: true\n  - type: textarea\n    id: feature-request\n    attributes:\n      label: 功能改进\n      description: 请详细描述需要改进或者添加的功能。\n      placeholder: \"功能改进\"\n    validations:\n      required: true\n  - type: textarea\n    id: references\n    attributes:\n      label: 参考资料\n      description: 可以列举一些参考资料，但是不要引用同类但商业化软件的任何内容。\n      placeholder: \"参考资料\"\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"gradle\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/gradle.yml",
    "content": "# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle\n\nname: Java CI with Gradle\n\non:\n  push:\n    branches: [ \"3.0\" ]\n  pull_request:\n    branches: [ \"3.0\" ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@v4\n    - name: Set up JDK 21\n      uses: actions/setup-java@v4\n      with:\n        java-version: '21'\n        distribution: 'temurin'\n\n    # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies.\n    # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md\n    - name: Setup Gradle\n      uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0\n\n    - name: Build with Gradle Wrapper\n      run: ./gradlew build\n\n    # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).\n    # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.\n    #\n    # - name: Setup Gradle\n    #   uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0\n    #   with:\n    #     gradle-version: '8.5'\n    #\n    # - name: Build with Gradle 8.5\n    #   run: gradle build\n\n  dependency-submission:\n\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n\n    steps:\n    - uses: actions/checkout@v4\n    - name: Set up JDK 21\n      uses: actions/setup-java@v4\n      with:\n        java-version: '21'\n        distribution: 'temurin'\n\n    # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.\n    # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md\n    - name: Generate and submit dependency graph\n      uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish package to the Maven Central Repository\non:\n  push:\n    tags:\n      - v*\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK 21\n        uses: actions/setup-java@v4\n        with:\n          java-version: '21'\n          distribution: 'temurin'\n\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v3\n\n      - name: Grant execute permission for gradlew\n        run: chmod +x gradlew\n\n      - name: Build without tests\n        run: ./gradlew build -x test\n\n      - name: Decode\n        run: |\n          echo \"${{secrets.SIGNING_SECRET_KEY_RING_FILE}}\" > ~/.gradle/secring.gpg.b64\n          base64 -d ~/.gradle/secring.gpg.b64 > ~/.gradle/secring.gpg\n\n      - name: Publish\n        run: ./gradlew publish -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=$(echo ~/.gradle/secring.gpg)\n        env:\n          MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}\n          MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "﻿# CHANGELOG\n\n## [v3.5.16] 2026.1.11\n- fix: 修复`Jackson3TypeHandler`自定义`ObjectMapper`无效\n- fix: 处理代码生成器`PackageConfig`指定模块为空时拼接错误\n- feat: 升级`SpringBoot3`至3.5.9\n- feat: 升级`JUnit`单元测试\n- feat: 升级`fastjson`至2.0.60\n- feat: 升级`jackson`至2.20.1\n- feat: 升级`gson`至2.13.2\n- feat: 升级`postgresql`至42.7.8\n- feat: 升级`h2database`至2.4.240\n- feat: 升级`mysql-connector-j`至9.5.0\n- feat: 升级`sqlite-jdbc`至3.51.1.0\n- feat: 升级`jaybird`至5.0.10.java8\n- feat: 升级`mybatis-spring`4.0.0\n\n## [v3.5.15] 2025.11.30\n- fix: 修复`Enjoy`模板生成xml错误\n- feat: 调整代码生成器元数据构建\n- feat: 优化`CrudRepository`批量执行前判断非事务中关闭连接\n- feat: 支持`SpringBoot`4.0.0\n- feat: 支持`Jackson`3.0\n\n## [v3.5.14] 2025.08.29\n- feat: 增加`bom`对`mybatis-plus-spring-boot4-starter`与`mybatis-plus-spring-boot4-starter-test`管理\n\n## [v3.5.13] 2025.08.29\n- fix: 修复在`Spring`中使用`@PostConstruct`调用`Db`方法查询出现警告日志\n- fix: 修复`Db`使用`count`返回`null`导致的空指针异常\n- fix: 修复`BaseMapper`在非`Spring`项目中报错\n- feat: 升级`Jsqlparser`5.2\n- feat: `OrderItem`增加`withExpression`根据表达式排序(注意:不支持序列化方式使用,自行控制`sql`注入)\n- feat: `OracleDdlGenerator`支持指定`schema`模式运行\n- feat: 适配华为云`GaussDb`数据库\n- feat: 雪花生成器初始化错误增加降级处理\n- feat: 新增`spring-boot4`支持\n- feat: 升级`gradle`8.13\n- opt: `SqlUtils`中`replaceSqlPlaceholder`方法增加缓存处理\n- opt：优化`DDL`执行记录表检查是否存在\n- opt: 容器环境下`workerId`获取优化\n\n## [v3.5.12] 2025.04.27\n- fix: 修复批量操作异步执行首次可能的出现`NoSuchElementException`错误\n- fix: 修复默认`SQL`解析线程池在`JVM`退出关闭导致的任务拒绝\n- fix: 修复`entity.java.btl`生成`toString`方法样式错误\n- fix: 修复`entity.java.ftl`模板类注释与导包缺少换行\n- opt: 重构`SqlRunner`执行`SQL`语句 (动态传参，不再根据参数值生成执行`SQL`)\n- opt: 增强`SqlRunner`执行(支持单参数使用`Map`({key}),`List`({index}),`JavaBean`({property})获取值)\n- opt: 改进`MybatisUtils`对自`SqlSessionFactory`的提取(支持自定义`SqlSessionTemplate`子类)\n- opt: 自动识别数据库支持`TDengine`数据库`websocket`连接\n- opt: 支持`Db`工具类对多数据源的支持\n- opt: 优化`MapperProxy`属性访问\n- opt: `CompatibleSet`接口增加`getBean`与`getProxyTargetObject`方法\n- opt: `CompatibleSet`与`CompatibleHelper`调整至`com.baomidou.mybatisplus.core.spi`包之中\n- opt: 支持手动指定`CompatibleSet`实现\n- opt: 代码生成器处理驱动返回索引信息`null`\n- opt: 代码生成器处理`PRIMARY_KEY_`为开头的主键索引情况\n- opt: 去除`entity.kt.btl`模板`@Override`注解\n- opt: 解决`serviceImpl.java.ej`生成格式不统一\n- opt: 去除`mapper.java.ftl`多余的换行生成\n- opt: 去除`entity.kt.vm`,`entity.kt.ej`,`entity.kt.btl`导包结束分隔符\n- opt: 去除`controller.java.ej`,`controller.java.vm`多余的换行\n- opt: 去除`entity.kt.btl`生成属性多余的空格\n- opt: 统一`entity.java.btl`,`entity.java.ej`,`entity.java.ftl`,`entity.java.vm` 生成的`toString`方法样式\n\n## [v3.5.11] 2025.03.23\n- fix: 修复代码生成器链式模型非`lombok`下生成了`@Accessors`注解\n- fix: 修复主键使用`UUID`执行批量删除错\n- fix: 修复`Kotlin`使用`select(predicate)`方法错误\n- fix: 修复`AbstractCaffeineJsqlParseCache`异步产生的错误\n- fix: 修复动态SQL解析包含SQL注释(--或#)导致的合并错误 (动态脚本语句不再处理换行,如果需要去除换行请自行处理)\n- fix: 修复`DataChangeRecorderInnerInterceptor`数据比较出现强转异常\n- fix: 修复`IllegalSQLInnerInterceptor`拦截插件获取`catalog`与`schema`错误\n- fix: 修复动态表解析`create table if not exists` 获取表名错误\n- fix: 修复动态表解析`create [type] index` 获取表名错误\n- feat: 新增`DynamicTableNameJsqlParserInnerInterceptor` 基于`JsqlParser`动态表处理\n- feat: 支持`DdlScript`自定义脚本运行器参数\n- feat: 支持`DdlHelper`自定义脚本运行器参数\n- feat: 支持`DdlApplicationRunner`参数配置(脚本错误处理,自定义`ScriptRunner`,多处理器执行异常是否中断)\n- feat: 支持`BaseMultiTableInnerInterceptor`指定追加条件模式 (默认条件追加至末尾,仅作用于`select`,`delete`,`update`)\n- feat: 支持生成器`Entity`指定`serialVersionUID`添加`@Serial`注解\n- feat: 支持生成器`Entity`注解(字段,类注解)自定义处理\n- feat: 支持生成器`Entity`导包自定义处理\n- feat: 支持`崖山`数据库\n- feat: 支持`Hive2`分页\n- feat: 升级`Gradle`至8.10\n- feat: 支持`DdlHelper`执行自定义异常处理\n- opt: 调整`DynamicTableNameInnerInterceptor`表处理逻辑并保证`hook`运行\n- opt: 调整`DdlScript`类方法实现(分离DDL版本记录,优化执行方法)\n- opt: 调整`DbType#GAUSS`数据库名为`gauss`\n- opt: 调整`JsqlParserGlobal`解析线程池指定\n- opt: 移除过时的`FieldStrategy.IGNORED`\n- opt: 移除过时的`GlobalConfig.DbConfig#selectStrategy`\n- opt: 移除过时的`MybatisSqlSessionFactoryBean#typeEnumsPackage`\n- opt: 优化`DdlHelper`资源加载(不再依赖`Spring`或者其他实现)\n- opt: 去除`DdlHelper`中`getScriptRunner`方法指定的字符集编码\n- doc: 修正`DdlHelper`中注释错误\n由于`jsqlParser`5.0版本与5.1版本升级不兼容性不是很大，计划后期移除`mybatis-plus-jsqlparser-5.0`支持模块。\n多版本支持相对来说比较麻烦，后期只维护`mybatis-plus-jsqlparser-4.9` 与 `mybatis-plus-jsqlparser`(保持最新版跟进,直到再提升jdk)\n\n## [v3.5.10.1] 2025.01.13\n- fix: 修复动态节点处理错误\n\n## [v3.5.10] 2025.01.12\n- fix: 修复字段有`TableField`注解但未指定`value`值下全局`columnFormat`未生效问题\n- fix: 修复enjoy模板生成kotlin代码报错\n- fix: 修复enjoy模板生成字符串代码报错\n- fix: 修复springdoc生成注解未转义双引号\n- fix: 修复数据变动插件更新无主键报错\n- fix: 修复多表解析processJoins解析表出现越界\n- feat: TableName注解新增`properties`属性\n- feat: 支持@InterceptorIgnore注解在default方法上\n- feat: 适配jsqlparser5.1版本(5.0兼容版本请使用`mybatis-plus-jsqlparser-5.0`)\n- feat: 提供`InterceptorIgnoreHelper.execute`模板执行方法处理插件跳过策略(防止手动使用handle方法出现未清理线程资源造成的错误)\n- feat: 代码生成器全局package配置属性支持自定义模板信息获取\n- feat: 代码生成器新增表索引信息获取\n- feat: 代码生成器提供`Mapper.Builder.generateMapperMethodHandler`处理器基于索引生成索引方法\n- feat: 代码生成器Entity支持自定义Class注解和字段注解生成\n- feat: 代码生成器Entity支持lombok模式指定生成类注解\n- feat: 代码生成器Entity支持ToString`(Entity.Builder.toString(boolean))`方法控制生成 (默认生成,lombok模式下将会生成@ToString,低版本下lombok不生成,属于不兼容改动)\n- feat: 代码生成器Entity支持字段文档注释(`Entity.Builder.fieldUseJavaDoc(boolean)`)控制生成 (默认生成,低版本下,使用swagger或springdoc不会生成字段文档注释,属于不兼容改动)\n- feat: 重写动态语句生成(生成执行SQL将不再包含\\n换行符)\n- feat: 安全加密处理器密钥获取支持环境变量与系统属性传入\n- feat: 升级mybatis至3.5.19\n- feat: 升级springboot至3.4.1\n- feat: 升级kotlin至2.1.0\n- 实用性低,检查语法不完善,计划移除IllegalSQLInnerInterceptor插件\n- 功能缺陷较多,计划移除DataChangeRecorderInnerInterceptor插件\n\n## [v3.5.9] 2024.10.23\n- opt: 优化代码生成器支持可视化配置生成能力\n- opt: 解耦扩展包不再强制依赖 spring 开发框架\n- opt: 拆分jsqlparser支持模块,提供mybatis-plus-jsqlparser(支持最新jsqlparser)与mybatis-plus-jsqlparser-4.9模块, 默认不携带,升级后需要自行引入.\n- feat: 重构 service 模块抽象为 CrudRepository 不再建议使用 IService 避免业务层数据混乱\n- feat: 新增 solon 启动插件支持\n- feat: 升级SpringBoot3.3.4\n- feat: 升级velocity2.4\n\n## [v3.5.8] 2024.09.18\n- fix: 解决optimizeJoinOfCountSql反序列化不支持问题\n- fix: 解决Db工具类批量操作使用rewriteBatchedStatements=true返回值不准确\n- fix: 修复逻辑删除填充与乐观锁冲突\n- fix: 修复IllegalSQLInnerInterceptor分析嵌套count语句错误\n- fix: 升级jsqlParser5.0 解决 for update 语句错误\n- fix: 修复处自增自减负数情况导致jsqlParser解析优化错误\n- fix: 修复removeMapper缓存清理不完全\n- fix: 修复SqlServerQuery查询表注释乱码\n- opt: 完善函数注入校验逻辑\n- opt: Page属性访问调整为private,重写toString方法\n- opt: 主键生成策略(uuid)不支持的类型打印警告日志\n- opt: MybatisPlusException转化为PersistenceException子类\n- feat: 增加deleteByIds空集合处理\n- feat: 重命名selectBatchIds方法为selectByIds\n- feat: 支持tableName与schema属性占位符配\n- feat: 代码生成器增加对虚拟列的属性获取\n- feat: chain wrapper to lambda chain wrapper #6314\n- feat: 代码生成器增加手动指定数据库驱动兼容不能自动注册的驱动实现\n- feat: 升级kotlin2.0.0\n- feat: 升级SpringBoot3.3.2\n- feat: 升级fastjson2.0.52\n- feat: 升级mybatis-spring3.0.4\n- feat: 升级spring-cloud-commons4.1.4\n- feat: 部分支持依赖升级更新\n- feat: 支持GoldenDB数据库\n- feat: 支持Duckdb数据库\n- feat: 支持Derby数据库\n- feat: 支持Vastbase数据库\n\n## [v3.5.7] 2024.06.10\n- fix: 修复动态表名处理 update ignore 错误\n- fix: 修复SQLServer2005分页处理空格错误\n- fix: 修复多租户查询出现问题\n- fix: 修正非通用泛型情况下序列化json减少强转\n- fix: 修复代码生成器禁用模板失效\n- fix: 修复分页count优化distinct搭配orderBy处理错误\n- fix: 修复达梦数据库生成代码错误\n- fix: 修复租户插件特殊exists语句会失效\n- fix: 修复sqlite数据库ddl_history错误导致无法创建表\n- fix: 修复DataChangeRecorderInnerInterceptor在Insert时配置忽略无效\n- fix: 修复代码生成器处理不标准的JdbcType导致空指针错误\n- feat: BaseMapper新增批量操作与InsertOrUpdate方法\n- feat: BaseMapper新增批量操作方法返回值List<BatchResult>\n- feat: BaseMapper方法逻辑删除默认支持填充\n- feat: 调整Service层逻辑删除填充逻辑处理\n- feat: 重构批量删除参数填充处理逻辑.\n- feat: 自增自减处理BigDecimal\n- feat: 新增雪花ID配置（支持手动分配workerId与datacenterId或指定网卡信息自动获取方式）\n- feat: 重构ServiceImpl泛型参数提取\n- feat: 修改AES密钥随机性生成\n- feat: UpdateWrapper增加checkSqlInjection方法\n- feat: 调整DDL脚本自动装配逻辑(当无实现时或无mybatis-plus-extension模块时不注入DDL运行bean)\n- feat: 注入方法deleteBatchIds重命名deleteByIds\n- feat: SpringBoot升级至2.7.18和3.2.6\n- feat: 升级kotlin至1.9.24\n- feat: 升级lombok至1.18.32\n\n## [v3.5.6] 2024.04.08\n- fix: 修复通用Service多层代理引发的错误\n- fix: 修复Json类型处理器反序列化泛型丢失原始类型\n- fix: 修复填充器处理器基本类型数组出现强制错误\n- fix: 修复上版本移除掉Page方法保留至PageDto类之中\n- fix: 修复IllegalSQLInnerInterceptor未处理Parenthesis\n- fix: 修复IllegalSQLInnerInterceptor表名或字段名包裹导致无法获取索引信息和索引字段校验问题\n- fix: 修复KtUpdateChainWrapper调用setSql的时候params没有展开\n- fix: 修复useGeneratedShortKey配置失效\n- fix: 修复DataChangeRecorderInnerInterceptor一系列问题\n- feat: 去除sqlFirst与sqlComment转义(如有需要转义操作,请手动调用转义后传入)\n- feat: ServiceImpl修改为抽象类,防止错误直接实例化\n- feat: 重构代码生成器TemplateConfig配置,模板禁用与路径配置更改至对应具体实现之上\n- feat: 支持组合注解\n- feat: 新增 LambdaUpdateWrapper 字段自增 setIncrBy 自减 setDecrBy 方法\n- feat: 获取注入方法时传递org.apache.ibatis.session.Configuration\n- feat: 新增自增主键兼容配置开关(mybatis-plus.global-config.db-config.insert-ignore-auto-increment-column 默认false,开启INSERT语句无视主键字段生成)\n- feat: 新增参数填充器跳过方式(基于MappedStatement#id)\n- feat: 新增SQLite的DDL自动维护功\n- feat: 新增eqSql方法\n- feat: 新增SQL解析线程池\n- feat: 增加雪花ID生成器初始化日志打印(默认超过5秒打印警告日志)\n- feat: 升级mybatis至3.5.16\n- feat: 升级spring-cloud-commons\n- feat: 升级jsqlparser至4.9\n- test: Github增加CI\n- doc: 增加update(Wrapper)相关api无法自动填充注释\n\n## [v3.5.5] 2023.12.24\n- fix: 修复配置databaseId失效\n- fix: 修复自增主键忽略注入错误忽略非自增主键注入问题\n- fix: 修复ChainWrapper模式下GroupBy生成多的逗\n- fix: 修复selectOne缓存问题\n- fix: 修复数据权限多表支持在某些场景下失效问题\n- fix: 修复生成器mysql类型转换器point转换错误\n- fix: 修复kotlin下无法使用父类属性操作\n- fix: 修复自动注入DdlApplicationRunner返回null导致的高版本springboot启动错误\n- fix: 修复生成器代码提示的RuntimeUtils安全性漏洞问题\n- feat: 新增fastjson2支持\n- feat: 升级gradle-wrapper至8.4\n- feat: 升级kotlin-gradle-plugin至1.9.21\n- feat: 升级mybatis至3.5.15\n- feat: 升级lombok至1.18.30\n- feat: 升级spring-boot3至3.2.0\n- feat: 升级spring-boot2版本mybatis-spring至2.1.2\n- feat: 升级spring-boot3版本mybatis-spring至3.0.3\n- feat: 移除通用service中saveOrUpdate的事务\n- feat: 支持Trino,Presto,GBase8s-pg,SUNDB数据库\n\n## [v3.5.4.1] 2023.11.4\n- fix: 修复Aop增强Mapper层导致的转换错误.\n\n## [v3.5.4] 2023.10.22\n\n- fix: 修复Insert无字段时执行SQL报错.\n- fix: 修复高版本JDK下lambda无法执行IDEA调试.\n- fix: 修复LambdaQuery中select,groupBy,orderBy,orderByAsc,orderByDesc提示的警告,新增对应doXxx方法支持重写(不兼容改动,api方法做了final处理).\n- fix: 修复inject-sql-session-on-mapper-scan无配置提示.\n- fix: 修复@OrderBy搭配@TableId排序字段错误(不兼容改动,com.baomidou.mybatisplus.core.metadata.TableInfo.orderByFields调整了类型).\n- fix: 修复Service中根据主键逻辑删除时类型不匹配导致的错误.\n- fix: 修复分页插件Count与自定义ResultHandler冲突.\n- fix: 修复字段填充处理器可能会出现重入问题\n- feat: 新增自增主键字段是否允许插入控制,可使用方法注入覆盖Insert(boolean ignoreAutoIncrementColumn)或Insert(String name, boolean ignoreAutoIncrementColumn)控制自增主键是否支持写入行为.\n- feat: ActiveRecord模式下deleteById(逻辑删除)方法支持自动填充功能.\n- feat: 内置泛型提取,支持非Spring体系项目使用.\n- feat: BaseMapper新增update(wrapper)更新方法.\n- feat: BaseMapper新增流式查询方法对大数据查询支持.\n- feat: 代码生成器元数据信息公开tableName与columnName字段访问.\n- feat: 新增mybatis-plus-spring-boot3-starter与mybatis-plus-spring-boot3-starter-test支持SpringBoot3.\n- feat: 支持插件缺省注入,当无MybatisPlusInterceptor注入时,支持com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor自动注入.\n- feat: 升级源码Jdk开发版本至Java21.\n- feat: 升级gradle-wrapper至8.4-rc-1.\n- feat: 升级kotlin-gradle-plugin至1.9.20-Beta.\n- feat: 升级SpringBoot2.x版本至2.7.15.\n- feat: 升级lombok至1.18.30.\n- opt: mybatis-plus-extension中mybatis-spring依赖修改为可选依赖(不兼容改动,如果项目在非spring或非springBoot下使用到了请手动添加依赖).\n- opt: spring-boot-starter减少无用的配置提示(不兼容改动,调整了com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties.configuration类型).\n- opt: 字段填充处理器提取去除固定参数提取,支持更宽松的mapper方法参数提取填充处理,\n- opt: 去除com.baomidou.mybatisplus.core.toolkit.ReflectionKit.setAccessible方法调用,防止高版本Jdk移除\n- opt: 调整selectOne方法(配合流式处理,最多提取两行数据,日志不再打印总记录数).\n- opt: 优化selectObjs方法返回值,减少类型强制转换.\n- opt: 通用Service支持多SqlSessionFactory注入.\n- opt: 优化TableInfo.newInstance创建实例方法.\n- opt: 去除多余的@SuppressWarnings(\"serial\")\n\n\n## [v3.5.3.2] 2023.08.08\n\n- feat: 升级mybatis至3.5.13,mybatis-spring至2.1.1\n- feat: jsqlparser提供统一解析类,可配置解析函数,并加入缓存选项\n- feat: 增加Sequence初始化debug日志\n- feat: 参数填充器支持多参数填充\n- feat: BaseMapper新增selectMaps(page, wrapper)与selectList(page, wrapper)方法\n- feat: 乐观锁字段支持 java.time.Instant\n- feat: `wrapper#apply`支持配置`mapping`比如`column={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}`\n- feat: 调整 QueryWrapper 需要主动开启检查 SQL 注入过滤（移除掉wrapper的orderby的sql过滤功能）\n- feat: 新增星瑞格数据库支持\n- feat: `updateWrapper#setSql`方法支持`动态入参`参考`wrapper#apply`方法\n- feat: 自动 SQL 维护 DDL 支持 SQL 执行存储过程\n- perf: `ktWrapper`加强泛型限制\n- fix: 修复在选择springdoc文档注释时entity描述异常\n- fix: 在主键的`IdType`为`AUTO`的情况下,`Table#getAllInsertSqlColumnMaybeIf(\"xx.\")`所生成sql错误问题\n- fix: 租户插件支持`update set subSelect`的情况\n- fix: 修复高版本Jdk提示非法反射警告(Illegal reflective access by com.baomidou.mybatisplus.core.toolkit.SetAccessibleAction)\n- fix: 修复高版本Jdk插件动态代理反射错误 (Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible)\n- fix: 修复路径替换将原有的“.”替换成了文件分隔符“/”\n- fix: 修复Beetl模板引擎无法生成注释\n- fix: 修复Types.DOUBLE类型无法映射\n- fix: 修复转换父类公共字段报错\n- fix: 修复生成器无法通过cfg.取值\n- fix: 修复单元测试下MockBean时事务回滚失败\n- fix: 修复Warpper类nonEmptyOfWhere方法命名不规范,导致Ognl未正确缓存带来的执行开销\n- fix: ClickHouseQuery类的tableComment()方法返回表注释字段为comment\n- fix: 修复在选择springdoc文档注释时entity描述异常问题\n- fix: Table\\#getAllInsertSqlColumnMaybeIf(\"xx.\")下的sql生成错误问题\n- fix: Db类增加根据实体不为空的字段条件查询方法重载\n- fix: 生成器对于Kotlin的Entity文件的superEntityClass的错误\n- fix: 修复springdoc freemarker模式下 表注释取值取不到\n- opt: 增强参数填充处理器,防止因参数名称与填充名称一致类型不匹配导致转换错误\n- opt: 优化方法注入,去除SelectPage,SelectMapsPage,SelectByMap,DeleteByMap注入\n- opt: 减少MappedStatement堆内存占用\n- opt: 解决PluginUtils重复获取元数据带来的性能消耗\n- opt: 注入方法去除多余的换行符\n- opt: 去除SqlRunner持有的sqlSessionFactory变量\n- opt: 解决Sequence初始化多次问题(自定义情况下可不创建默认主键生成器)\n- opt: 优化 SqlHelper#getMapper 返回泛型\n- opt: 去除SqlRunner持有的sqlSessionFactory变量\n- docs: 修正DdlHelper注释错误\n\n\n## [v3.5.3.1] 2022.12.29\n\n- bug:生成模块pg和dm语句模式名增加\n- feat: 优化 ChainWrapper#getEntityClass\n- fix: 修复在 IService.lambdaQuery().one() 使用场景在数据库无数据时报错问题\n- est 以及租户插件解析sql遇到多表必须给表起别名\n\n\n## [v3.5.3] 2022.12.28\n\n- 多租户插件:多表join表名必需起别名,否则追加的过滤条件不带前缀\n- InterceptorIgnore 不能过滤 selectKey 的问题\n- 分页新增`informix数据库`支持\n- 分页新增`优炫数据库`支持\n- 分页新增`TDengine数据库`支持\n- 分页新增`亚马逊redshift数据库`支持\n- 支持spring-boot 2.7以上版本\n- 雪花id新增反解时间戳方法`Sequence#parseIdTimestamp`\n- BaseMapper.selectCount生成语句加入中`AS total`\n- 修复IllegalSQLInnerInterceptor类ClassCastException异常，并优化日志\n- 移除注解`OrderBy`的过时属性`isDesc`\n- 移除`TableInfo`过时方法\n- 加入`JoinTableInfoInitHandler`类参与`TableInfo`初始化\n- 修复StringUtils.sqlInjectionReplaceBlank方法过滤sql不全，可能会导致sql注入的情况\n- 增加IService.lambdaQuery(entity)支持，写法更便捷\n- 新增数据变更记录（数据审计）插件`DataChangeRecorderInnerInterceptor`\n- 新增查询条件方法 notLikeLeft 和 notLikeRight\n- 数据权限多表解析部分处理优化\n- 允许子类重写 orderBy 基础方法 gitee issues/I61F51\n- 新增Db类，调整 SimpleQuery 类\n- 新增脚本自动维护功能\n- 新增支持手动拦截器忽略策略，例如 `InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());`\n- 支持 PG 数据字段大写 ID 自增 fixed issues/I4T0YJ\n- 代码生成器重构完成，合并回 MP 核心代码库\n- 代码生成器增加是否生成service接口的开关\n\n\n## [v3.5.2] 2022.06.01\n\n- 升级 mybatis 3.5.10\n- 升级 jsqlparser 4.4\n- 添加 vertical 数据库分页支持\n- 添加对Gbase 8s 数据库支持\n- 添加对 行云 数据库分页的支持\n- 添加对 Firebird 数据库分页的支持\n- 修复参数填充判断错误，标记替换字段常量\n- DbType 清理以及 IDialect 实现类的清理\n- 新增SqlHelper.execute，通过entityClass获取BaseMapper\n- 枚举处理优化,不再需要'typeEnumsPackage'这个配置\n- fix 租户id获取的执行顺序\n- 新增Firebird数据库的KeyGenerator\n- 新增达梦Dm数据库的KeyGenerator\n- Merge pull request #4343 from LK820/fix-IdType.java\n- Merge pull request #4495 from nieqiurong/fix-parameter\n- Merge pull request #4314 from tomalloc/3.0\n\n\n## [v3.5.1] 2022.01.25\n\n- 新增 impala 数据库支~~持\n- 缓存动态获取数据库类型~~\n- 新增可控分配 id 方法 fixed github pull/4231\n- 延迟枚举扫描注册\n- 乐观锁插件支持根据wrapper填充 github pull/3664\n- H2KeyGenerator 语法修改\n- SimpleQuery 优化及Bug修改\n- fixed gitee issues/I4P9EN\n- SybaseDialect 关键词替换优化\n\n\n## [v3.5.0] 2022.01.01\n\n- 升级 mybatis 3.5.9\n- 升级 jsqlparser 4.3\n- 新增移除 Mapper 相关缓存，支持 GroovyClassLoader 动态注入 Mapper\n- 添加动态表名的钩子函数 https://github.com/baomidou/mybatis-plus/pull/3965\n- 注入类 DefaultSqlInjector 优化调整\n- 反射类 ReflectionKit 优化 field -> field 改为 Function.identity()\n- baseMapper 新增方法 exist 方法\n- 解决 sysbase 小写 from 导致 index 取不到正确的索引值问题\n- 新增通过 entityClass 获取 Mapper 方法 `BaseMapper<Entity> mapper = SqlHelper.getMapper(Entity.class);`\n- 注入方法 byId 注入优化\n- 多租户 right join bug https://gitee.com/baomidou/mybatis-plus/issues/I4FP6E  https://github.com/baomidou/mybatis-plus/pull/4035\n- 自定义注入方法名优化 https://github.com/baomidou/mybatis-plus/pull/4159\n- 新增 sap hana 内存数据库\n- 新增 SimpleQuery 工具栏查询\n- SQL 注入验证工具类 代码修改写法\n- 整理字符串常量的使用\n- upgrade license-gradle-plugin version\n- 自定义注入方法名优化 (不兼容)\n- 重载columnsToString方法允许子类调整\n- 修复 et 判断逻辑 fixed gitee issues/I4L4XV\n- 逻辑删除 byId 支持转换为实体删除填充\n\n\n\n## [v3.4.3.4] 2021.09.22\n\n- order by wrapper 存在条件不排序问题处理\n- 解决引入 cloud InetUtils 类编译错误\n- 升级 sql 解析依赖 jsqlparser 版本 4.2\n- fix: 修复 JDK16 中增加模块化校验后，导致 lambda 序列化失败问题\n- fix: java 17 的支持 #I4A7I5\n- bug: fix left join 条件构造会多一个的问题\n- fix: 当逻辑删除字段默认值为null时，阻止全表更新插件失效\n- 分页 count(*) as total\n- 允许注入自定义事务工厂 TransactionFactory\n\n\n## [v3.4.3.3] 2021.09.05\n\n- 移除不在实用类 ISqlParserFilter AbstractJsqlParser 需要使用从旧版本复制\n- 移除全局配置workerId，datacenterId参数，推荐直接初始化identifierGenerator\n- count 方法 Integer 修改为 Long 涉及升级成本【注意】，对于涉及缺陷调整给您造成困扰表示抱歉\n- 修复主键 @0rderby 注解 bug\n- 修复 String 主键删除失败\n- 主键类型增加 BigDecimal BigInteger 支持\n- 隔离 spring 框架强依赖，非 spring 框架用 mp 注入 GenericTypeUtils.setGenericTypeResolver\n\n\n## [v3.4.3.2] 2021.08.21\n\n- 增加 goldilocks 数据库 csiidb 数据库 的支持\n- 增加对南大通用GBase 8s数据库的支持（GBASEDBT)，区别于原有定义（GBASE)\n- 优化 selectOne 查询方式，精简 SQL 注入\n- PropertyMapper.whenNotBlack to whenNotBlank\n- BaseMapper新增deleteById(T entity)方法\n- jsqlparser 版本 4.0 升级 4.1\n- TableInfo新增原生Reflector反射操作.\n- 解决 lambda 构造器在 JDK16 中无法运行的问题\n- wrapper clear 将sqlSegment重置为空串 缓存标志重置为true\n- 注入器调整无主键不注入ById方法\n- 自动构建 resultMap 处理主键获取真正的字段名\n- Wrapper optimized: 优化警告\n- Wrapper 新增 gtSql geSql ltSql leSql 方法\n- 新增对CUBRID数据库的支持\n- fix github pull/3557 乐观锁新增版本号 null 自定义异常，租户插入忽略逻辑允许自定义\n- fix github issues/2931 解决结果集大于 Integer 异常问题\n- fix github issues/3652 k8s 网络获取失败问题\n- fix gitee issues/I3Z2RG 优化 Order By SQL 注入识别率\n- fix gitee issues/3826 优化动态表名处理器\n- fix gitee issues/I3UQH5 修复注解@OrderBy，使用limit 异常\n- fix github issues/3768 mysql 批量自增 bug\n- 修复自动构建resultMap时主键字段映射错误&OrderBySegmentList懒加载执行\n- 源代码升级相关测试依赖，构建环境 gradle 升级为 7.1 新增更多测试用例\n\n\n## [v3.4.3.1] 2021.06.15\n\n- 支持多重继承获取泛型\n- 应要求 pageDto 修改为 PageDTO\n- 分页排序优化\n- TableField 新增 ResultMapping#property 注解支持\n- fixed github pull/3550 优化排序\n- fix #I3T0LA\n- 开放KtUpdateChainWrapper、KtQueryChainWrapper的继承\n- 新增 exists 方法判断 count 存在\n- 优化数据方言获取方式减少对象创建\n- feat GlobalConfig增加whereStrategy属性和适配selectStrategy的getWhereStrategy()方法\n- 扩展 p6spy 优化\n- fix github#3390 SqlRunner.selectPage()方法未释放连接克隆\n- 优化 JDK 默认不推荐泛型数组\n- perf: 替换为 JVM 中本身的方法\n- 当用户指定ID时，不用自动生成，不指定时自增\n- Github Merge pull request #3549 #3555 #3565 #3571 #3587 #3591 #3592 #3595 #3599 #3605 #3606\n- 提供处理Map多key取值工具方法\n- 调整 page 注解泛型 E 为 P 方便阅读\n- Pattern定义为静态常量，优化正则匹配速度\n- Fix 主键添加@OrderBy无效\n- 去除addMappedStatement日志打印\n- NoKeyGenerator Jdbc3KeyGenerator shared instance\n\n## [v3.4.3] 2021.05.21\n\n- 增加瀚高数据库支持\n- 增加注解 Order By 支持默认排序\n- Wrapper exists notExists orderBy groupBy 支持参数绑定\n- Wrapper 支持 setParamAlias 其它优化\n- 优化 KeyGenerator 支持多实现多数据源注入\n- 增强 ServiceImpl 泛型推断，解决多继承与代理问题\n- 新增 PageDto 用于微服务对象传输序列化\n- 新增 Page 提供静态 of 构造方式\n- 增加代理 MethodHandleProxies 对 lambda 调试支持\n- 调整 ActiveRecord 日志对象初始化\n- 调整 ActiveRecord 模式 Model 类开发 pkVal 方法外部可用\n- 删除标记过时代码\n- 优化枚举值获取方式\n- 分页 count 安全处理\n- Sequence 方法支持重写支持\n- 升级 Mybatis 3.5.7\n- 修复自动配置 lazy-initialization 无属性提示\n- 修复 mysql on duplicate key update 字段名判断为表名问题\n- 修复 lambda 条件 npe 异常\n- 重构 lambda 信息提取方法\n- 获取 lambda 信息不在序列化\n- 合并 gitee pulls/ 141\n- fixed github issues/3208 3016\n- fixed github issues/3482 数据权限处理器支持 union all\n- 调整事务未启用打印提示信息\n- 单元测试优化相关依赖升级\n\n## [v3.4.2] 2021.01.15\n\n- fix: 移除 BlockAttackInnerInterceptor 内引用的 commons 的 utils\n- feat: PaginationInnerInterceptor 添加 optimizeJoin 属性控制是否在count时对sql的join进行优化\n- feat: 可通过Resources.setDefaultClassLoader设置默认类加载器.\n- feat: InterceptorIgnore 注解新增 others 属性\n- feat: IService 增加 kotlin 链式调用支持(ktQuery() 和 ktUpdate())\n- style: jsqlparser up to 4.0\n- style: 移除 com.baomidou.mybatisplus.extension.injector.methods.additional 包下的过时类\n- style: generator 模块另开仓库 [generator](https://github.com/baomidou/generator)\n\n## [v3.4.1] 2020.11.10\n\n- fix: 新多租户插件完善子查询,支持 比较符号,in,EXISTS,NOT EXISTS\n- feat: 公开 AbstractWrapper.getEntityClass\n- feat: 新增 FakeTenantLineInnerInterceptor 对 TenantSqlParser 进行过度\n- feat: 分页count识别 `left join (subSelect)` 优化\n- feat: 所有 count 从 count(1) 变更为 count(*)\n- style: mybatis up to 3.5.6\n\n## [v3.4.0] 2020.8.23\n- fix: @TableName.autoResultMap=true 情况下, 内置的 selectBody 将不会进行 as ,使用了的需要注意!!!\n- feat: 新增 mybatis-plus-boot-starter-test 模块\n- fix: MetaObjectHandler 重载错误(解决办法是参数位置对调),填充值在泛型上支持字段类型的子类\n- feat: mybatis up to 3.5.5, mybatis-spring up to 2.0.5\n- feat: jsqlparser up to 3.2\n- feat: 新增 MybatisParameterHandler, 废弃 MybatisDefaultParameterHandler\n- feat: 分页插件加入 GBase,ClickHouse,oscar,OceanBase 数据库连接自动识别的支持\n- feat: Wrapper 新增api not(boolean condition, Consumer consumer)\n- feat: 新增 MybatisPlusInterceptor 解决 多租户和分页 插件一级和二级缓存不正确问题\n- feat: 新分页插件优化 size<0 时继续拼接 orderBy\n- feat: 新增 IdentifierGenerator 一个实现类 ImadcnIdentifierGenerator\n- fix: chainWrapper#func 强转异常\n- fix(mybatis-plus-generator.main): 重构生成器数据库类型转换器，修复部分支条，提交选择器测试\n- fix: 修复复杂情况中动态表名替换产生的问题：正则由空白检测转为单词边界检测\n- refactor: 重构动态表名解析器，去除正则替换程序，改为按表名位置进行替换\n- refactor: 将表名解析重构为访问者模式，现在不会对原有 SQL 做改动\n\n\n## [v3.3.2] 2020.5.26\n- 分页参数提取,单元测试用例修复\n- 达梦数据库代码生成器表过滤支持\n- 微软数据库代码生成器表过滤支持\n- 修复代码生成器属性字段规则错误\n- SelectById 支持自定义方法名\n- 修复分页插件获取数据库类型问题\n- Json转换器空值处理\n- bugfix(mybatis-plus-generator):SQL类型返回错误问题\n- 调整未知方言异常,自动识别url转换小写匹配.\n- fix: 初始化 TableInfo 中遇到多个字段有 @TableId 注解时未能抛出异常的问题\n- SuperController有Class参数的set方法\n- 增加方法StrategyConfig.setSuperServiceImplClass(java.lang.Class<?>).\n- 代码生成器命名策略调整.\n- 扩展分页缓存key值计算.\n- 去除方法推测,直接访问属性字段.\n- 修正枚举处理器类型不匹配比较.\n- 修改表前缀匹配方式\n- 修改在Mybatis全局配置文件中设置分页插件参数不生效问题\n- 修复PR未指定解析器的时候引发空指针\n- 增加分页插件limit参数配置\n- 修复指定superEntityClass重复生成父类字段问题\n- 无主键的情况无需导入IdType与TableId包\n- 调整生成BaseResultMap格式\n- 支持lombok模式下选择是否进行链式set生成\n- 修复解析器for update错误\n- 过滤PG约束列(只留下主键约束)\n- 增加生成器禁用模板生成\n- fix(kotlin): 修复动态表名 BUG，最大努力替换表名\n- 修复PG约束生成重复属性字段问题\n- fix(kotlin): 将 LambdaUtils 中缓存的 key 改为 String\n- 代码生成器增加数据库关键字处理接口\n- fix github/issues/2454 支持注解可继承\n- 新增 AES 加密数据库用户名密码\n- 优化方法入参泛型，支持更多类型\n- 修复代码生成器开启移除is前缀生成实体缺少包导入\n- fixed github issues/2470\n\n\n## [v3.3.1] 2020.1.17\n- 新增`TableName`注解属性`excludeProperty`支持排除字段\n- 新增ServiceImpl#entityClass属性，减少泛型提取\n- 新增phoenix支持\n- 新增支持hbase的选装件`Upsert`\n- 新增生成器策略配置enableSqlFilter属性来控制是否启用SQL过滤表支持\n- 新增批量执行方法，方便用户自定义批量执行操作\n- `Wrapper`支持`clear`清空\n- `Wrapper`子类新增`func`方法,主要为了支持在`if else`情况下使用`Wrapper`的不同method不会导致断链(链式调用不能一链到底)\n- `BaseMapper`部分入参为`Wrapper`的select方法支持`wrapper.first`来设置RDS的hint\n- `KtUpdateWrapper#set`支持value为null\n- 支持泛型主键支持\n- 优化分页拦截器数据类型与方言实现类配置\n- 二级缓存复用count查询缓存\n- `IService`部分method调整为default方法\n- 二级缓存兼容json序列化情况（主要出现默认缓存count出现long反序列化回来为int的情况）\n- 处理批量操作嵌套事物问题（二级缓存更新问题）\n- 修复启用乐观锁下updateById时自动填充不生效的问题\n- 修复自动填充接口的default方法(`setFieldValByName`和`getFieldValByName`)某些情况下会发生异常的问题\n- 修复`KtWrapper`嵌套函数问题\n- 修复Freemarker生成Kotlin类的常量错误\n- 修复StringUtils#guessGetterName错误\n- 修复SerializationUtils资源未释放问题\n\n## [v3.3.0] 2019.12.06\n- BaseMapper 接口两个 page 方法优化\n- IService 以及 ServiceImpl 对应 page 方法优化,个别返回 collection 的方法修改为返回 list\n- 逻辑删除字段的两个表示已删除和未删除的定义支持字符串 `\"null\"`\n- 修复批量操作未清空缓存\n- 批量操作异常转换为DataAccessException\n- mybatis up 3.5.3, mybatis-spring up 2.0.3, jsqlparser up 3.1\n- mapper 选装件包调整, chainWrapper 包调整\n- 新增 ChainWrappers 工具类\n- 新增 IdentifierGenerator 接口,支持自定义Id生成\n- 代码生成工具废弃正则表名匹配,新增likeTable与notLikeTable\n- 分页插件支持自定义处理页数限制与溢出总页数处理\n- 修复SqlExplainInterceptor导致的Oracle序列自增两次\n- 分页二级缓存支持\n- 扩展p6spy日志打印\n- DbConfig加入新属性propertyFormat,TableFieldInfo移除属性related\n- 优化序列生成器,过时KeySequence的clazz属性\n- 修复Ognl表达式关键字导致的null值判断失效\n- 修复更新填充开关失效\n- 优化填充逻辑\n- ISqlRunner支持selectPage\n- 支持全局逻辑删除字段\n- BaseMapper的方法可自定义\n- 添加【虚谷】【Oracle12c】【Kingbase】数据库支持\n- 解决数据库字段与实体字段名称不同时出现`null as xxx`的情况\n- 过时ID_WORKER_STR,自动识别主键类型\n- 配置开启注解，TableName也强制生成\n\n## [v3.2.0] 2019.08.26\n- 代码生成器添加达梦数据库支持\n- 修复多主键查询表字段SQL的Bug\n- 新增 updateWrapper 尝试更新，否继续执行saveOrUpdate(T)方法\n- 代码生成器 pg 增加 numeric instant 类型支持\n- 修复InjectionConfig不存在时无法生成代码的问题\n- fix: #1386(github) 逻辑删除字段为Date类型并且非删除数据日期为null\n- 升级依赖 mybatis 版本为 3.5.2\n- 升级依赖 jsqlparser 版本为 2.1\n- 应 EasyScheduler 计划提交 Apache 孵化请求移除 996NPL 协议限制\n- 调整 SQL 移除 SET 部分 Github/1460\n- 移除 SqlMethod 枚举 UPDATE_ALL_COLUMN_BY_ID 属性，推荐使用 AlwaysUpdateSomeColumnById 套\n- fix: #1412(github) github:mybatis-plus-generator can't support oracle\n- fix: github 1380\n- 移除全局配置的 dbType 和 columnLike\n- 移除 fieldStrategy, 使用上个版本新增的三个替代\n- 移除 PerformanceInterceptor 相关, 建议使用 p6spy\n- 移除 el 拆分为 jdbcType typeHandler 等具体属性\n- 升级 gradle-5.5.1,lombok-1.18.4\n- 当selectStatement.getSelectBody()的类型为SetOperationList\n- 移除 GlobalConfig#sqlParserCache 属性,移除 LogicSqlInjector, OrderItem 新增2个快捷生成的method, page 新增一个入参是 List<OrderItem> 的 addOrder method\n- Nested 接口个别入参是 `Function<Param, Param> func` 的method,入参更改为 `Consumer<Param> consumer`,不影响规范的使用\n- fixed gitee/I10XWC 允许根据 TableField 信息判断自定义类型\n- Merge pull request #1445 from kana112233/3.0\n- 支持过滤父类属性功能\n- 添加批量异常捕获测试\n- 多租户ID 值表达式，支持多个 ID 条件查询\n- 扩展新增 json 类型处理器 jackson fastjson 两种实现\n\n\n## [v3.1.2] 2019.06.26\n- EnumTypeHandler 更名为 MybatisEnumTypeHandler,移除 EnumAnnotationTypeHandler\n- 新增自动构建 resultMap 功能,去除转义符\n- 注解增加变量控制是否自动生成resultmap\n- 修改分页缓存Key值错误\n- TableField.el 属性标记过时\n- 取消 MybatisMapWrapperFactory 的自动注册\n- starter 增加默认xml路径扫描\n- 新增 MybatisPlusPropertiesCustomizer 及配置使用\n- ConfigurationCustomizer 内部方法入参更新为 MybatisConfiguration\n- 原有 fieldStrategy 标记过时,新增 3 种 fieldStrategy 进行区分\n- 获取注入方法时传递当前mapperClass\n- 增加sqlite代码自动生成测试代码及测试用的数据库文件\n- JsqlParserCountOptimize 对 left join 的 sql 优化 count 更精确\n- fix(AbstractWrapper.java): 修复 lambda 表达式在 order、groupBy 只有条件一个时引起的类型推断错误\n- apply plugin: 'kotlin'\n- refactor(order): 修复排序字段优先级问题(#IX1QO)\n- 启动就缓存 lambdacache\n- Merge pull request #1213 from sandynz/feature/sqlComment 支持SQL注释\n- 去除 wrapper 的一些变量,wrapper 内部 string 传递优化\n- fix: #1160(github) 分页组件orderBy: 同时存在group by 和order by，且IPage 参数中存在排序属性时，拼接\n- Merge pull request #1253 from ShammgodYoung/patch-1 代码生成器输入表名忽略大小写\n- 新增渲染对象 MAP 信息预处理注入\n- 修改 dts rabbitAdmin bean 判断方式\n- Merge pull request #1255 from ShammgodYoung/patch-2 对serialVersionUID属性进行缩进\n- JsqlParserCountOptimize 加入 boolean 字段,判断是否优化 join\n- Merge pull request #1256 from baomidou/master Master\n- freemarker entity 模板缩进调整\n- 增加jdbcType,typeHandler属性, 合并el属性\n\n\n## [v3.1.1] 2019.04.25\n- 新增 996icu license 协议\n- 新增 mybatis-plus-dts 分布式事务 rabbit 可靠消息机制\n- 新增 DynamicTableNameParser 解析器、支持动态表名\n- 优化 getOne 日志打印\n- sql 优化跳过存储过程\n- 优化分页查询(count为0不继续查询)\n- 修复分页一级缓存无法继续翻页问题\n- MybatisMapWrapperFactory 自动注入\n- 支持纯注解下使用 IPage 的子类作为返回值\n- 逻辑删除不再需要 LogicInject\n- GlobalConfig 加入 enableSqlRunner 属性控制是否注入 SqlRunner ,默认 false\n- SqlParser注解不再需要全局设置参数才会缓存,以及支持注解在 mapper 上\n- GlobalConfig 的 sqlParserCache 设置为过时\n- mybatis 升级到 3.5.1 , mybatis-spring 升级到 2.0.1 , jsqlparser 降级到 1.2\n- ISqlInjector 接口 移除 injectSqlRunner 方法\n- SqlFormatter 类设置为过时\n- 解决自动注入的 method 的 SqlCommandType 在逻辑删除下混乱问题\n- 新增 AlwaysUpdateSomeColumnById 选装件\n- SFunction 继承 Function\n- DbConfig 的 columnLike 和 dbType 属性设置为过时\n- DbConfig 新增 schema 和 columnFormat 属性\n- TableField 注解增加 keepGlobalFormat 属性\n- TableName 注解增加 schema 和 keepGlobalPrefix 属性\n- fixed bug tmp文件格式错乱 github #1048\n- 处理表/字段名称抽象 INameConvert 接口策略 github #1038\n- DB2支持动态 schema 配置 github #1035\n- 把字段缓存的key从className替换成了.class, 如果使用dev-tools会导致：MybatisPlusException: Your property named \"xxxx\" cannot find the corresponding database column name!(解决方案：去掉dev-tools)\n\n\n## [v3.1.0] 2019.02.24\n- 升级 `mybatis` 到 `3.5.0` 版本\n- 升级 `mybatis-spring` 到 `2.0.0` 版本\n- 升级 `jsqlparser` 到 `1.4` 版本\n- 新增 p6spy 日志打印支持\n- 变更 `IService` 的 `getOne(Wrapper<T> queryWrapper)` 方法如果获取到多条数据将会抛出 `TooManyResultsException` 异常\n- 修复 自定义分页功能不支持注解 `@select` 问题\n- 修复 生成器的配置 kotlin 模式下 swagger 模式无效问题\n- 修复 生成器 is 开头字段无法自动注解问题\n- 修复 生成器 Serializable Active 模式继承父类包自动导入异常问题\n- 修复 生成器 支持公共字段自动读取父类 class 属性问题\n- 修复 枚举(注解方式)转换器在存储过程中转换失败\n- 修复 beetl 模板逻辑删除注解错误问题\n- 修复 通过 `mybatis-config.xml` 方式构建的 `Configuration` 的 `mapUnderscoreToCamelCase` 默认值非 `true` 的问题\n- 修复 sql解析器动态代理引发的bug\n- 修复 `mapper` 使用纯注解下可能触发的重试机制在个别情况下启动报错的问题\n- 优化 支持指定 `defaultEnumTypeHandler` 来进行通用枚举处理\n- 优化 从 hibernate copy 最新代码到 SqlFormatter\n- 移除 `wrapper` 的 `in` 以及 `notIn` 方法内部对入参 `coll` 及 `动态数组` 的非empty判断(**注意: 如果以前有直接使用以上的方法的入参可能为 empty 的现在会产出如下sql: `in ()` 或 `not in ()` 导致报错**)\n- 移除 `wrapper` 的 `notInOrThrow` 和 `inOrThrow` 方法(**使用新版的 `in` 以及 `notIn` 效果一样,异常则为sql异常**)\n- 移除 `IService` 的 `query` 链式调用的 `delete` 操作\n- 移除 xml 热加载相关配置项,只保留`MybatisMapperRefresh`该类并打上过时标志\n- 日常优化\n\n## [v3.0.7.1] 2019.01.02\n- 修复 lambdaWrapper 的获取不到主键缓存的问题\n- 优化 `IService` 新增的 `update` 链式调用支持 `remove` 操作\n- 过时 `IService` 新增的 `query` 链式调用的 `delete` 打上过时标识\n- 日常优化\n\n\n## [v3.0.7] 2019.01.01\n- 优化 generator 的 postgresSql 数据库支持生成 java8 时间类型\n- 优化 generator 的 sqlServer 数据库支持生成 java8 时间类型\n- 优化 LambdaWrapper 反射获取字段信息支持首字母大写的字段\n- 优化 仅 LambdaWrapper 的 select 优化(支持字段对不上数据库时自动 as)\n- 优化 重复扫描 `BaseMapper` 子类时,`TableInfo` 缓存的 `Configuration` 只保留最后一个\n- 优化 `MergeSegments` 获取 `getSqlSegment` 方式\n- 优化 SQL 自动注入器的初始化 modelClass 过程,提高初始化速度\n- 优化 `BaseMapper` 的 `update` 方法的第一个入参支持为 `null`\n- 新增 `IService` 增加4个链式调用方法\n- 新增 代码生成器增加 `beetl` 模板\n- 新增 `IdWorker` 增加毫秒时间 ID 可用于订单 ID\n- 新增 wrapper 新增 `inOrThrow` 方法,入参为 empty 则抛出 `MybatisPlusExcuption` 异常\n- 新增 `MetaObjectHandler` 新提供几个能根据注解才插入值的 `default` 方法\n- 新增 kotlin 下 lambda 的支持,`KtQueryWrapper` 和 `KtUpdateWrapper`类\n- 新增 简化MP自定义SQL使用方法,现在可以使用 `自定义sql` + ${ew.customSqlSegment} 方式\n- 新增 提供新的 `InsertBatchSomeColumn` 选装件\n- 修复 Page` 的 `setTotal(Long total)` -> `setTotal(long total)`\n- 修复 `Page` 的 `setSearchCount` 为 `public`\n- 修复 `TenantSqlParser` 如果 where 条件的开头是一个 `orExpression`，直接在左边用and拼接租户信息会造成逻辑不符合预期的问题\n- 修复 wrapper 的 `lambda` 方法会向下传递 sqlSelect\n- 修复 `ServiceImpl` 个别 batch 操作 `flushStatements` 问题\n- 修复 selectObjs 泛型错误问题\n- 移除 `InsertBatchAllColumn` 选装件\n- 移除 `ServiceImpl` 的 batch 操作之外的事务注解\n- 移除 `Model` 的事务注解\n- 移除 `AbstractSqlInjector` 的 `isInjectSqlRunner` 方法(SqlRunner初始化较早，目前isInjectSqlRunner无法控制)\n- 移除 `MybatisSessionFactoryBuilder`\n- 移除 对 `mybatis-plus-generator` 包的依赖,自己按需引入\n- 还原 xml 热加载,打上过时标识\n- 升级 jsqlparser 依赖到 1.3\n- 日常优化\n\n\n## [v3.0.6] 2018.11.18\n- 修复entity中2个以上条件并且拼接ODER BY 或 GROUP BY 产生的 WHERE X1 =? AND X2\n- refactor(SerializedLambda.java):重构方法增加反序列化安全性，优化命名\n- 基础Mapper优化支持自定义父类Mapper构造自己需要的注入方法\n- 使用<where><set>代替<trim>\n- 部分优化: 直到抛出异常时才进行字符串 format\n- 优化 IdWorker 生成UUID使用并发性能\n- feat: 动态分页模型、优化分页方言重新修正db2分页语句\n- Assert 支持 i18n 多语言错误提示\n- 支持 total 控制是否 count sql 新增 isSearchCount 方法\n- feat: move spring dependency from core module to extension\n- fix: Junit.assertTrue\n- 强制使用自定义ParameterHandler,去除byId类型限制.\n- 新增选装件的 InsertBatch 通用方法,以及相应测试,以及代码和性能的优化\n- IPage 新增功能,泛型转换\n- 自动填充判断填充值是否为空,为空时跳过填充逻辑\n- batchsize 阈值设 30 修改为 1000 提升效率\n- 修复在极端情况下saveOrUpdate执行错误\n- 移除 MybatisSqlSessionTemplate\n- 移除 xml 热加载\n- 其他优化\n\n\n## [v3.0.5] 2018.10.11\n- 移除 ApiAssert 改为 Assert\n- 移除 ApiResult 改为 R\n- SQL 注入器优化\n- 移除 excludeColumns 方法\n- 修复 last 方法的 condition 入参不生效的问题\n- 修复去除1=1 BUG\n- 移除对 spring-devtools 的支持\n- 修复实体属性都为null时Sql拼接出错问题\n- 缓存Class反射信息,提升效率\n- 继承Model类的实体中,现在无需重写pkVal()方法\n- 解决在设置了config-location的情况下报mpe的bug,以及优化初始化逻辑\n- 修复存在 mapper.xml 情况下逻辑删除失效\n- 调整 关于ServiceImpl中的事务问题 gitee issue/IN8T8\n- 修复 DB2分页方言 github issues/526\n\n\n## [v3.0.4] 2018.09.28\n- 修正全局配置 FieldStrategy 为非默认值\n- 修正批量事务异常问题\n- Api 层 R 类自动处理逻辑失败\n- 修改h2脚本初始化加载,去除测试用例注入.\n- 新增注释其它\n\n\n## [v3.0.3] 2018.09.17\n- 新增筛选查询字段方法\n- fixed orderBy多入参的bug\n- 新增 LogicDeleteByIdWithFill 组件\n- fixed github issues/476 issues/473\n- fixed github issues/360 gitee issues/IMIHN IM6GM\n- 改进 allEq入参的value改用泛型\n- fixed saveOrUpdateBatch使用BatchExecutor\n- fixed 修正getOne获取多条数据为抛出异常\n- 修正service 的getOne 方法\n- 修正service 的个别方法为default方法\n- 修复了page在set了desc下,sql有bug的问题\n- 去除不再需要的方法\n- 解决 generator 的 optional 的俩 jar 问题\n- 重载 select(Predicate<TableFieldInfo> predicate)\n- 其他优化\n\n\n## [v3.0.2] 2018.09.11\n- 新增 Wrapper 条件辅助类\n- 新增 banner 属性控制是否打印\n- 修复 gitee #IMMF4:批量插入(AR)事务无效\n- fix: entity 无主键,生成 ew 的 where 条件的 bug\n- 处理SqlRunner的sqlSession获取与释放\n- 去除全局缓存sqlSession,增加Model,通用service层sqlSession释放\n- ext: 抽象原生枚举处理类注册，方便扩展\n- 优化扩展性其他\n\n\n## [v3.0.1] 2018.08.31\n- 修复代码生成器设置表前缀异常\n- 新增 EnumValue 注解方式扫描通用枚举处理\n- 修复逻辑删除混用失败\n- DB2 方言改进何鹏举优化\n- 新增测试用例及其他\n\n\n## [v3.0-RELEASE] 2018.08.28 代号：超级棒棒糖 🍭\n- 乐观锁 update(et,ew)方法 et带上 version 注解字段回写\n- 优化改进优化代码生成器\n- 包扫描为空时不抛出异常(枚举,别名）\n- 去除 SqlSession\n- 修改 issue 模板,完善注释\n- 优化初始化过程,添加逻辑删除注解次数检测\n- SQL检查允许跳过检查\n- 支持达梦数据库\n- 修改 code 为数值型严谨限制简化 api 层命名及初始值规则\n- 初始化 SQL 解析移至 SqlInjector\n- 其他代码优化\n\n\n## [v3.0-RC3] 2018.08.19 代号：超级棒棒糖 🍭 RC3\n- 支持 TableField select 属性 false 排除默认注入大字段查询\n- 解决 page 反序列化 pages 属性报错\n- 合并2.x dataSource被代理处理\n- 去除DbConfig.columnUnderline属性\n- 过滤掉selectObjs查询结果集为空的情况\n- baseMapper 的 insert 和 update 返回值不再使用包装类\n- fixed Gitee issues/IM3NW\n- 优化代码完善注释等\n\n\n## [v3.0-RC2] 2018.08.10 代号：超级棒棒糖 🍭 RC2\n- 生成器加回 MODULE_NAME 开放配置 config\n- 修复setting - defaultEnumTypeHandler属性配置无效\n- 兼容 Spring boot 1.x 启动.\n- 日常优化 , 测试用例 , 优化抛出异常的过程\n- 新增 Gitee Github issue,pull_request模板\n- 移除数据库关键字转义, 只支持注解模式转义\n- 优化掉抛异常为使用 assert 或者 exceptionUtils\n- 设置下划线转驼峰到 configuration 优化 ColumnUnderline\n- 解决 page 序列化 asc desc 多态序列化异常\n- 默认的 dbType 改为 other, 如果用户没有配置才会自动获取 dbType\n- 优化,ColumnUnderline与MapUnderscoreToCamelCase意义相同\n- fixed ILY8C 生成器指定 IdType 场景导入包\n- 补充注释新增大量测试用例\n\n\n## [v3.0-RC1] 2018.08.01 代号：超级棒棒糖 🍭 RC1\n- 优化工具类部分代码，并修复一个在多线程环境下可能会引发死锁的BUG\n- 新增断言类,顺便修改几处地方的判断抛异常为使用断言\n- 去掉多余的 \"implements Serializable\"\n- 魔法值都改为全局常量模式\n- 咩咩说了 MP 3.0 分页已经飘飘欲仙了，不在需要迁就使用 PageHelper 模式\n- issue #384 QueryWrapper 支持排除指定字段模式\n- 全新 banner，全新感觉\n- 再优化一下抛异常的过程\n- 修改 class 实例化对象的方式，现在可以实例化私有 class\n- 支持无配置可启动使用 Gitee issues/ILJQA\n- 释放sqlSession,待优化 ActiveRecord单元测试\n- 解决只调用 last 产生的 sql 会出的问题\n- 修复Lambda首位属性为基类属性时错误.\n- 增加泛型限制,格式化下代码.\n- 优化一下 AbstractWrapper 使用的 ISqlSegment\n- 其他\n\n\n## [v3.0-RC] 2018.07.23 代号：超级棒棒糖 🍭 RC\n- 优化 page 当 size 小于 0 自动调整为 list 模式\n- 新增 攻击 SQL 阻断解析器\n- 优化解析核心方法名，新增 querywrapper lambda 转换参数测试\n- 调整通用 service 层方法命名为阿里规范 （ 小白鼠，对不起，请唾弃我们吧！然后修改下您的项目。）\n- 代码生成器允许正则表达式匹配表名\n- 乐观锁 回写更新后的version到实体\n- Github #385:查询动态表名能利用Wrapper\n- 修复 Gitee issues/ILEYD\n- Page 的序列化接口挪到 IPage 接口\n- 解决了 gamma 不能自动赋值 ID\n- 代码改个常量引用优化\n\n\n## [v3.0-gamma] 2018.07.15 代号：超级棒棒糖 🍭 伽玛\n- IPage 新增 listMode 集合模式\n- fixd gitee issues/IL7W4\n- fixed gitee issues/IL7W4\n- 优化生成器包导入\n- 解决 Page ascs，descs 异常\n- 逻辑删除无法 set where entity 一个参数并存逻辑\n- 合并 PR 修改typeAliasesPackage扫描多维度\n- 完善 3.0 测试用例\n- 代码性能优化及其他\n\n\n## [v3.0-beta] 2018.07.07 代号：超级棒棒糖 🍭 贝塔\n- 新增字段 LIKE 查询注入全局配置，默认 true 开启\n- 修改 dbtype 的 oracle db2 修改 CONCAT 方式\n- 修正无论 update 的入参 updateWrapper 如何变化,逻辑删除下依然存在限制条件\n- 注释加上告警，完善注释\n- 修复 github issues/377 378 389\n- 解决逻辑删除同时存在非逻辑删除逻辑\n- 逻辑删除支持 delete set 其他字段，update 排除逻辑删除字段\n- 支持 typeAliasesPackage 多项每项都有通配符 com.a.b.*.po, com.c.*.po\n- 修复 gitee issues/IKJ48 IL0B2\n- 其他完善\n\n\n## [v3.0-alpha] 2018.07.01 代号：超级棒棒糖 🍭\n- 升级 JDK 8 + 优化性能 Wrapper 支持 lambda 语法\n- 模块化 MP 合理的分配各个包结构\n- 重构注入方法，支持任意方法精简注入模式\n- 全局配置下划线转换消灭注入 AS 语句\n- 改造 Wrapper 更改为 QueryWrapper UpdateWrapper\n- 重构 分页插件 消灭固定分页模型，支持 Mapper 直接返回 IPage 接口\n- 新增 Rest Api 通过 Controller 层\n- 实体 String 类型字段默认使用 LIKE 查询 SelectOne 默认 LIMIT 1\n- 辅助支持 selectMaps 新增 bean map 互转工具类\n- 增加 db2 支持 starter 改为 Spring boot 2+ 支持\n- 重构生成器提供自定义 DB 多种模板引擎支持\n- 相关 BUG 修复\n\n\n## [v2.1.9] 2018.01.28 代号：怀念（纪念 2017 baomidou 组织小伙伴 MP 共同成长之路，奔向 2018 旺旺旺）\n- page 分页新增控制是否优化 Count Sql 设置\n```\n// 不进行 count sql 优化\npage.setOptimizeCountSql(false);\n```\n- 注入定义填充，支持sql注入器,主键生成器.\n- fixed github issues/231\n- fixed github issues/234\n- 修改逻辑删除 selectByIds coll 问题\n- fixed gitee issues/IHF7N\n- fixed gitee issues/IHH83\n- 兼容配置方式,优先使用自定义注入.\n- 其他优化\n\n\n## [v2.1.9-SNAPSHOT] 2018.01.16\n- 调整 Gradle 依赖模式\n- IdType 可选 ID_WORKER_STR `字符串类型` IdWorker.getIdStr() 字符串类型\n- TableField 注解新增属性 `update` 预处理 set 字段自定义注入 fixed gitee IHART\n```\n 例如：@TableField(.. , update=\"%s+1\") 其中 %s 会填充为字段\n 输出 SQL 为：update 表 set 字段=字段+1 where ...\n```\n```\n 例如：@TableField(.. , update=\"now()\") 使用数据库时间\n 输出 SQL 为：update 表 set 字段=now() where ...\n```\n- TableField 注解新增属性 `condition` 预处理 WHERE 实体条件自定义运算规则\n```\n@TableField(condition = SqlCondition.LIKE)\nprivate String name;\n输出 SQL 为：select 表 where name LIKE CONCAT('%',值,'%')\n```\n- 添加 spring-boot-starter 模块内置 `jdbc mp 包不需要单独引入` 更舒服的使用 boot\n- 添加对 SQL Server 视图生成的支持\n- 允许字段策略独立设置，默认为 naming 策略\n```\nstrategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略\nstrategy.setColumnNaming(NamingStrategy.underline_to_camel);// 允许字段策略独立设置，默认为 naming 策略\n```\n- 代码生成器抽象 AbstractTemplateEngine 模板引擎抽象类，可自定义模板引擎，新增内置 freemarker 可选\n```\n// 选择 freemarker 引擎\nmpg.setTemplateEngine(new FreemarkerTemplateEngine());\n```\n- 相关 SQL 解析如多租户可通过 `@SqlParser(filter=true)` 排除 SQL 解析\n```\n# 开启 SQL 解析缓存注解生效\nmybatis-plus:\n    global-config:\n        sql-parser-cache: true\n```\n- 解决xml加载顺序问题，可随意引入其他 xml sql 片段\n- 修复 author 带123的bug\n- fix #IGQGE:Wrapper为空,但是page.getCondition()不为空的情况,Condition无法传递问题\n- fix #IH6ED:Pagination dubbo 排序等属性序列化不支持\n- 判断Wrapper是否为空，使用==，避免被equals方法重载的影响\n- 避免注入自定义基类\n- 剥离 sql 单独提出至 SqlUtils\n- 统一缩进编码风格\n- 优化生成代码执行性能 github issues/219\n- 优化 sql 解析过程\n- fixed gitee issues/IHCQB\n- springboot-configuration-processor 修改 compileOnly为optional\n- 其他\n\n\n## [v2.1.8] 2018.01.02 代号：囍\n- 修复代码生成器>字段前缀导致的bug\n- 使用类全名替代手写的全名\n- build修改\n- 脚本警告,忽略目录\n- 其他优化\n\n\n## [v2.1.8-SNAPSHOT] 2017.12.28 代号：翻车鱼（秋秋赐名）\n- 返回Map自动下划线转驼峰\n- kotlin entity 静态常量支持\n- 优化 pagination 构造模式\n- Merge pull request #201\n- fix: selectByMap @alexqdjay\n- 增加sqlRuner测试用例，修复selectObjs只获取一个字段的bug\n- 新增 BlobTypeHandler\n- 去掉参数map的初始大小配置\n- 增加.editorconfig,模板空格问题修复.\n- Hikaricp连接池无法打印sql\n- 全局中去掉了路径,mapperLocations不可缺少了.\n- k 神 全部覆盖测试用例\n\n\n## [v2.1.7] 2017.12.11 代号：清风徐来 ， 该版本号存在 bug 请改为 2.1.8-SNAPSHOT +\n- 枚举处理：基本类型，Number类型，String类型\n- IGDRW:源码注释错误，容易给人误导 注释错误问题\n- 炮灰 PR !42:添加分页构造方法重载 添加分页构造方法重载\n- 代码生成 > oracle > 解决超出最大游标的问题\n- fixed gitee IGNL9\n- k 神 一大波 testcase 来袭\n- 使用transient关键字去除Page中部分字段参与序列化\n- 去除无效日志\n- fix #IGI3H:selectBatchIds 参数改为Collection类型\n- bugfix for logic delete sql injector\n- 添加多个排序字段支持\n- fixed github #185:2.0.2版本 自增主键 批量插入问题 pr\n- 其他优化`\n\n\n## [v2.1.6] 2017.11.22 代号：小秋秋之吻\n- 模块拆分为 support core generate 代码生成分离可选择依赖\n- 解决 gitee issue IFX30 拆分 mybatis-plus-support 包支持\n- 解决 gitee issue IGAPX 通用枚举 bigdecimal 类型映射\n- druid补充,填充字段修改\n- 修复 kotlin 代码生成部分逻辑 Bug\n- 合并 gitee pr 40 updateAllColumn****等方法排除fill = FieldFill.INSERT注释的字段 感谢 Elsif\n- 构造模式设置 kotlin 修改\n- Sql 工具类反射实例优化\n- 其他优化\n\n\n## [v2.1.5] 2017.11.11 代号：离神\n- 通用枚举 spring boot 兼容调整\n- PostgreSQL 支持关键词非关键词转换问题\n- Cat73  PR 稍微调整下自动生成的代码\n- 支持 kotlin 代码生成\n- bugfix for metaObj handler set val which not included in ...\n- alibaba 规范调整\n- 其他\n\n\n## [v2.1.3 - 2.1.4] 2017.10.15\n- 新增通用枚举处理器，参考 spring boot demno\n- 优化 SQL 解析器\n- 新增 schema 租户解析器待完善\n- 其他优化\n\n\n## [v2.1.2] 2017.09.17 代号： X\n- 修复代码生成器 Bug\n- fixed gitee issues/IF2DY\n- 修改 page 可链式操作\n- 去掉转义 oracle\n- fixed github issues/119\n- fixed gitee issues/IF2OI\n\n\n## [v2.1.1] 2017.09.12 代号：小锅盖\n- 修改分页超过总记录数自动设置第一页 bug @wujing 感谢 pr\n- fixed IEID6\n- 升级 mybatis 3.4.5\n- 升级生成器模板引擎 veloctiy 2.0\n- 升级 jsqlparser 1.1\n- 新增 SQL 解析链可动态扩展自定义 SQL 解析\n- 新增 多租户 SQL 解析逻辑，具体查看 spring boot 演示 demo\n- jasonlong10 PR 性能分析拦截器 支持OraclePreparedStatementWrapper的情况打印 SQL\n- fixed github issues/145\n- fixed gitee issue/IF1OF\n- add sqlSelect(\"distinct test_type\") test case\n- 添加填充生成器遗漏 TableField 导入类\n- fixed github issues/MYSQL表名含有保留字代码生成时报错 #124:字段全为 大写 下划线命名支持\n- fixed github issues/134\n- PostgreSQL 代码生成支持指定 schema 表字段按照默认排序\n- 其他优化调整\n\n\n## [v2.1.0] 2017.08.01 代号：小秋秋\n\n####主体功能\n- 批量sqlSession没有关闭问题修复\n- 处理sql格式化报错问题,添加填充信息\n- #91:关于insertBatch在大数据量的时候优化 github\n- 新增 uuid 主键测试用例\n- 修复BUG自动填充会覆盖之前的值\n- 升级pom依赖，spring-test作用域test\n- 更改sqlServer驱动,去掉乐观锁不需要的string类型测试\n- #86:关于plus的底层映射设计问题 github issue\n- SqlHelper处理Wrapper为空,但是page.getCondition()不为空的情况\n- Merge pull request !33:生成实体增加字段排序 from 老千/master\n- 解决使用代理对象导致无法获取实例缓存信息\n- 解决布尔类型is开头生成sql错误问题\n- DBType设置错误\n- fix #351:DB2Dialect返回NULL\n- fix #356:自动代码生成的Boolean类型的get方法不对\n- fix #353:代码生成@TableLogic问题\n- 新增 PostgreSqlInjector 自动注入器，处理字段大小写敏感，自动双引号转义。\n- 仓库地址与用户信息使用自定义传入.\n- fix #357:代码生成@TableLogic引入包Bug\n- Sequence 新增 mac 判断，分页 pageHelper 模式新增 freeTotal() 方法\n- #95:分页插件俩个建议 Github, selectItems contains #{} ${},\n- 添加 Wrapper#setSqlSelect(String... columns) 方法,方便通过自动生成的实体...\n- fixed github 116 issue\n- fixed osgit IE436  IDVPZ  IDTZH\n\n####代码生成\n- 修改实体生成模板\n- 修复自动填充代码生成错误\n- 新增 postgresql schemaname 生成器支持\n- 调整序列化导入问题\n- 其他\n\n\n## [v2.1-gamma] 2017.06.29\n\n####主体功能\n- 修正之前sqlserver自动获取类型错误问题\n- 修复用户无法自定义分页数据库方言问题\n\n####代码生成\n- 完善了自动填充代码生成\n- 修复postgresql生成重复字段问题\n\n####上个版本（2.0.9）升级导致的问题\n- 修复实体主键不在第一位无法读取的问题\n- 修复在自定义insert操作时报`Insert not found et`异常，见#331\n- 修复Sql生成错误问题(普通注入Group,Having,Order)\n- 修复逻辑删除生成Sql顺序错误\n- 感谢各路小伙伴及时反馈的问题,上个版本给大家带来的问题深感抱歉\n\n###Mybatis-Plus-Boot-Start [1.0.4]\n\n####主体变动\n- 去除Mybatis-plus直接依赖\n- 去除SpringBoot jdbc-starter直接依赖\n\n## [v2.0.9] 2017.06.26 代号：K 神\n###Mybaits-Plus\n####主体功能\n- 修正乐观锁和逻辑删除冲突问题\n- 处理在生成注入SQL时之前没有考虑到存在且打开下划线配置情况\n- 修复EntityWrapper继承关系问题\n- Wrapper添加条件判断\n- 性能分析插件支持记录日志提示\n- Wrapper重写了toString方式,解决之前Debug时显示为null给用户造成错觉\n- 处理Sequence非毫秒内并发偶数居多问题\n- 忽略策略优化处理更改了注解的属性\n- 注入Sql的方式优化,去除之前XML注入方式\n- 处理逻辑删除出现2个Where的问题\n- 添加其他数据库序列的实现方式,并开放出接口给用户自行扩展\n- 乐观锁优化调整\n- 优化Wrapper中Where AND OR 去除之前基于反射方式实现,提高代码运行效率\n- 处理不添加mybatis-config.xml主键无法填充问题\n- MybatisPlus添加支持gradle构建方式\n- Wrapper 添加 `and()` `or()` 方法\n- 优化GlobalConfiguration,抽离出GlobalConfigUtils减少耦合\n- 修复Sqlserver2008与SqlServer2005分页问题\n- 新增自动识别数据库,减少用户显式配置\n- 优化分页插件减少用户显示配置属性\n- 自动填充字段问题解决\n- 新增PageHelper,获取当前线程来管理分页(之前老用户最好不要使用,改方式只用户适用MybatisPageHelper用户习惯)\n- 大幅度的添加测试用例(感谢K神支持)\n- 代码的其他优化\n- 添加了JSqlparser的依赖以后不用手动去添加该Jar包\n\n####代码生成\n- 支持逻辑删除方式生成\n- 支持乐观锁方式生成\n- 修复生成器不能识别sqlServer的自增主键代码生成器不能识别SqlServer自增主键的问题\n- 支持Lombok方式生成\n- 支持构建模式方式生成\n- 添加Clob和Blob类型转换\n- 修复Oracle的Number类型字段转换错误问题\n\n###Mybatis-Plus-Boot-Start [1.0.2] 代号：清风\n####主体功能\n- 处理AR模式devtool替换数据源失效问题\n- 添加逻辑删除支持\n- 添加序列支持\n\n## [v2.0.8] 2017.05.15\n- Wrapper添加设置对象sqlSelect\n- 兼容无注解情况\n- 乐观锁去除默认short实现,优化绑定注册器在扫描阶段绑定. 测试改为h2环境.\n- 优化热加载,去除mapper路径配置.\n- 减少刷新Mapper配置\n- 修复tableFiled value 为空情况，开启下划线命名\n- sequence 升级提示\n- 开放表信息、预留子类重写\n- 修改Idwork测试\n- 支持 devtools\n- fixed 259 支持 xml resultMap 公共字段生成\n- fixed pulls 28 支持属性重载\n\n\n## [v2.0.6  2.0.7] 2017.04.20\n- 新增 逻辑删除\n- 新增 Oracle Sequence\n- 新增 jdk1.8 时间类型\n- 完善支持乐观锁\n- 完善字段填充器，支持更新填充\n- 升级 mybatis 依赖为 3.4.4\n- 代码调整优化，支持 wrapper limit 等逻辑\n- 修复 Id 策略 auto bug ，生成器 bug 其他\n\n\n## [v2.0.5] 2017.03.25\n\n- 修复分页连接池没有关闭的bug\n- issues fixed 217\n- IMetaObjectHandler当主键类型是AUTO或者INPUT的时候不起效的bug\n- 修复 like 占位符问题\n- 生成代码的时候如果目录不存在则新建\n\n\n## [v2.0.3 - v2.0.4] 2017.03.22\n\n- 优化Wrapper代码结构\n- 优化原有数据库连接获取\n- 解决Page初始化问题(之前只能通过构造方法生效,现在可以通过setget也可以生效)\n- 支持乐观锁插件\n- 改造Wrapper让JDBC底层来处理参数,更好的与PreparedStatement结合\n- 修复相关错误日志提示级别\n- Wrapper开放isWhere方法,现在可以自定义是否拼接\"WHERE\"\n- JDK版本向下兼容,之前相关代码用到了1.7新特性,当前版本解除\n- sqlserver生成bug修复以及代码优化\n- 优化MybatisPlus,SqlSession获取\n- 解决未配置切点的情况下获取的sqlSession提交不属于当前事务的问题以及多个sqlSession造成的事务问题\n- 增强执行sql类,sqlRunner\n- Model添加序列化ID,避免以后在修改Model后没有设置序列号ID时序列号ID可以会变动的情况\n- 添加重写默认BaseMapper测试用例\n- 感谢各路小伙伴提问的好的建议以及贡献代码,就不一一点名了\n\n\n## [v2.0.2] 2017.02.13\n- 修复全局配置不起作用 2.0.1 逻辑\n- 去除byId强制配置类型\n- Wrapper Page 等程序优化\n- 优化AR模式自动关闭数据库连接(之前需要手动设置事务)\n- 优化代码生成器，下划线名称注解不处理驼峰，支持自定义更多的模板例如 jsp html 等\n- 新增 service 层测试\n- sql日志记录整合至性能分析插件.\n- 处理多数据源分页插件支持多重数据库\n\n\n## [v2.0.1] 2017.01.15\n\n- 解决EntityWrapper对布尔类型构造sql语句错误\n- 全局配置初始化日志提示调整\n- Mybatis依赖升级至3.4.2,Mybatis-Spring依赖升级至1.3.1\n- Service中补充方法(selectObjs,selectMaps)\n- 解决selectCount数据库返回null报错问题\n- 支持PostgreSql代码生成\n- 拓展支持外部提供转义字符以及关键字列表\n- 开放数据库表无主键依然注入MP的CRUD(无主键不能使用MP的xxById方法)\n- 解决EntityWrapper拼接SQL时,首次调用OR方法不起作用的问题\n- sqlServer代码生成(基于2008版本)\n- 解决生成代码时未导入BigDecimal问题.\n- 释放自动读取数据库时的数据库连接\n- 优化全局校验机制(机制为EMPTY增加忽略Date类型)\n- 优化注入,避免扫描到BaseMapper\n- 优化注入,去除多余注入方法\n- SQLlikeType改名为SqlLike\n- 解决热加载关联查询错误问题\n- SqlQuery改名为SqlRunner\n- 优化完善代码生成器\n- 修复代码生成器未导入@tableName\n- 全局配置需要手动添加MP的默认注入类,更改为自动注入简化配置\n- Wrapper增加ne方法\n- 修复Mybatis动态参数无法生成totalCount问题\n- 代码结构优化，生成器模板优化\n- 解决issus[138,140,142,148,151,152,153,156,157]，具体请查看里程碑[mybatis-plus 2.0.1 计划](https://gitee.com/baomidou/mybatis-plus/milestones/2)中所有issus\n\n## [v2.0.0] 2016.12.11\n\n- 支持全局大写命名策略\n- 自动分页Count语句优化\n- 优化现有全局配置策略\n- 优化全局验证策略\n- 优化代码生成器(之前硬编码，现使用模板形式)\n- 优化注入通用方法ByMap逻辑\n- 添加自动选择数据库类型\n- 改善SqlExplainInterceptor（自行判断MySQL版本不支持该拦截器则直接放行（版本过低小于5.6.3））\n- 修复部分特殊字符字符多次转义的问题\n- 优化现有EntityWrapper添加Wrapper父类以及Condition链式查询\n- Wrapper类使LIKE方法兼容多种数据库\n- 优化日志使用原生Mybatis自带的日志输出提示信息\n- 修复使用缓存导致使用分页无法计算Count值\n- 修复PerformanceInterceptor替换`?`导致打印SQL不准确问题，并添加格式化SQL选项\n- 添加多种数据库支持，请查看DBType\n- 添加字符串类型字段非空校验策略（字符串类型自动判断非空以及非空字符串）\n- Wrapper添加类似QBC查询(eq、gt、lt等等)\n- 支持AR模式（需继承Model）\n- 合并所有Selective通用方法（例如:去除之前的insert方法并把之前的insetSelective改名为insert）\n- 解决sql剥离器会去除`--`的情况\n- 支持MySQL关键词，自动转义\n- 精简底层Service、Mapper继承结构\n- 不喜欢在XML中写SQL的福音，新增执行SQL方式，具体请查看SqlQuery\n- 优化代码结构\n- 解决issus[95,96,98,100,103,104,108,114,119,121,123,124,125,126,127,128,131,133,134,135]，具体请查看里程碑[mybatis-plus 2.0 计划](https://gitee.com/baomidou/mybatis-plus/milestones/1)中所有issus\n\n## [v1.4.9] 2016.10.28\n\n- ServiceImpl去除@Transactional注解、去除Slf4j依赖\n- 解决使用EntityWrapper查询时，参数为特殊字符时，存在sql注入问题\n- 调整Mybatis驼峰配置顺序 MybatisPlus > Mybatis\n- 优化分页插件并修复分页溢出设置不起作用问题\n- 去除DBKeywordsProcessor，添加MySQL自动转义关键词\n- 代码生成器新增支持TEXT、TIME、TIMESTAMP类型生成\n- 新增批量插入方法\n- 代码生成器新增Controller层代码生成\n- 调整EntityWrapper类部分List入参为Collection\n- 代码生成器优化支持 resultMap\n\n## [v1.4.8] 2016.10.12\n\n- insertOrUpdate增加主键空字符串判断\n- 支持Mybatis原生驼峰配置 mapUnderscoreToCamelCase 开关设置\n- 支持 TableField FieldStrategy 注解全局配置\n- SelectOne、SelectCount方法支持EntityWrapper方式\n- oracle 代码生成器支持 Integer Long Dobule 类型区分\n- 修复INPUT主键策略InsertOrUpdate方法Bug\n- EntityWrapper IN 添加可变数组支持\n- 基础Mapper、Servcie通用方法PK参数类型更改至Serializable\n- 当selectOne结果集不唯一时,添加警告提示(需开启日志warn模式)\n- baseService添加logger,子类直接调用logger不用重新定义(需slf4j依赖)\n\n## [v1.4.7] 2016.09.27\n\n- 主键注解 I 改为 PK 方便理解，去掉 mapper 注解\n- 性能分析插件，特殊处理 $ 符内容\n- 添加自动提交事务说明，新增事务测试\n- 支持 resultMap 实体结果集映射\n- 增加#TableField(el = \"\")表达式，当该Field为对象时, 可使用#{对象.属性}来映射到数据表、及测试\n- 新增 typeHanler 级联查询支持\n- 新增验证字段策略枚举类\n- 代码生成器支持实体构建者模型设置\n- 代码生成器新增实体常量生成支持\n- CRUD 新增 insertOrUpdate 方法\n- 解决MessageFormat.format格式化数字类型sql错误\n- EntityWrapper添加 EXISTS、IN、BETWEEN AND(感谢D.Yang提出)方法支持\n- 支持 mysql5.7+ json enum 类型，代码生成\n- 支持无XML依然注入CRUD方法\n- 修改Mybatis原生配置文件加载顺序\n\n## [v1.4.6] 2016.09.05\n\n- 新增无 @TableId 注解跳过注入SQL\n- 支持非表映射对象插入不执行填充\n- xxxByMap 支持 null 查询\n\n## [v1.4.5] 2016.08.28\n\n- 新增 XML 修改自动热加载功能\n- 添加自动处理EntityWrapper方法中的MessageFormat Params类型为字符串的参数\n- 新增表公共字段自动填充功能\n\n## [v1.4.4] 2016.08.25\n\n- entitywrapper所有条件类方法支持传入null参数，该条件不会附件到SQL语句中\n- TSQLPlus更名为TSqlPlus与整体命名保持一致。\n- 修复mysql关键字bug----将关键字映射转换加上``符号，增加xml文件生成时可自定义文件后缀名\n- 关闭资源前增加非空判断,避免错误sql引起的空指针错误,增加选择 current>pages 判断\n- TSQL 相关类实现序列化支持 dubbo\n- 增加 mybatis 自动热加载插件\n- 支持数据库 order key 等关键词转义 curd 操作\n\n## [v1.4.3] 2016.08.23\n\n- 优化 Sequence 兼容无法获取 mac 情况\n- 兼容用户设置 ID 空字符串，自动填充\n- 纯大写命名，转为小写属性\n- 修改EntityWrapper符合T-SQL语法标准的条件进行方法封装定义\n- 升级 1.4.3 测试传递依赖\n\n## [v1.4.0] 2016.08.17\n\n- 增加自定义 select 结果集，优化 page 分页\n- 未考虑 函数，去掉 field 优化\n- 新增 delete update 全表操作禁止执行拦截器\n\n## [v1.3.9] 2016.08.09\n\n- 修复 bug\n- 解决插入 map 异常\n- 插入 map 不处理，原样返回\n- 优化 IdWorker 生成器\n- 支持自定义 LanguageDriver\n- 支持代码生成自定义类名\n- 升级 mybatis 3.4.1 依赖\n\n## [v1.3.6] 2016.07.28\n\n- 支持全局表字段下划线命名设置\n- 增加自定义 注入 sql 方法\n- 优化分页总记录数为0不执行列表查询逻辑\n- 自动生成 xml 基础字段增加 AS 处理\n- 支持字段子查询\n\n## [v1.3.5] 2016.07.24\n\n- 升级 1.3.5 支持全局表字段下划线命名设置\n- 添加发现设置多个主键注解抛出异常\n- 添加无主键主键启动异常\n- 去掉重置 getDefaultScriptingLanuageInstance\n- 修改歧义重载方法\n\n## [v1.3.3] 2016.07.15\n\n- 处理 SimpleDateFormat 非现场安全问题\n- 修改 oracle 分页 bug 修复\n- oracle TIMESTAMP 生成支持 bug 修复\n\n## [v1.3.2] 2016.07.12\n\n- service 暴露 sqlSegment 的方法调用\n- 新增 sql 执行性能分析 plugins\n- 新增 deleteByMap ， selectByMap\n\n## [v1.3.0] 2016.07.07\n\n- 支持 like 比较等查询 sqlSegment 实现\n- 支持 typeAliasesPackage 通配符扫描, 无 count 分页查询\n- mybatis mapper 方法调用执行原理测试\n- 添加 IOC 演示用例\n\n## [v1.2.17] 2016.06.15\n\n- 优化 代码生成器 感谢 yanghu pull request\n- 调整 sql 加载顺序 xmlSql > curdSql\n- 支持 CURD 二级缓存\n- 增加缓存测试，及特殊字符测试\n\n## [v1.2.15] 2016.04.27\n\n- 新增 支持oracle 自动代码生成，测试 功能\n- 新增 UUID 策略\n- 演示demo 点击 spring-wind\n- 新增支持单表 count 查询\n\n## [v1.2.12] 2016.04.22\n\n- 添加 service 层支持泛型 id 支持，自动生成代码优化\n- 升级 mybatis 为 3.4.0 ，mybatis-spring 为 1.3.0\n\n## [v1.2.11] 2016.04.18\n\n- 新增批量更新，支持 oracle 批量操作\n- 去掉，移植至 spring-wind 的文档\n- 支持 jdk1.5 修改 param 描述\n- 添加数据库类型\n\n## [v1.2.9] 2016.04.10\n\n- EntityWrapper 新增无 order by 构造方法\n- MailHelper 重载 sendMail 方法\n- 新增 String 主键ID 支持 CommonMapper\n- 原来方法 selectList 分离为 selectList ， selectPage 两个方法\n- 优化代码生成器，添加文档说明、其他\n\n## [v1.2.8] 2016.04.02\n\n- 优化生成代码处理大写字段，支持自动生成 entity mapper service 文件\n- 优化分页 index 超出逻辑，新增 5 个 CRUD 操作方法\n- 开放模板引擎 getHtmltext 方法\n- 优化邮件发送配置添加说明文档\n- 添加文档说明、其他\n\n## [v1.2.6] 2016.03.29\n\n- 优化代码 service 层封装，抽离 list 、 page 方法\n- 优化分页 count sql 语句\n- 改进 mail 工具类\n- 完善 framework 对 spring 框架的支持\n- 添加文档说明、其他\n\n## [v1.2.5] 2016.03.25\n\n- 独立支持id泛型的 baseMapper\n- 更完善的自动生成工具\n- 支持实体封装排序\n- 分页插件完善\n- 抽离 service 主键泛型支持\n\n## [v1.2.2] 2016.03.14\n\n- 注解 ID 区分 AUTO 数据库自增，ID_WORKER 自动填充自定义自增ID , INPUT 手动输入 。\n- 优化代码及自动生成器功能。\n- 其他\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright (c) 2011-${year}, baomidou (jobob@qq.com).\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MPCodeStyle.xml",
    "content": "<code_scheme name=\"JavaCodeStyle\">\n  <option name=\"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"99\" />\n  <option name=\"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"99\" />\n  <option name=\"PACKAGES_TO_USE_IMPORT_ON_DEMAND\">\n    <value />\n  </option>\n  <option name=\"IMPORT_LAYOUT_TABLE\">\n    <value>\n      <package name=\"\" withSubpackages=\"true\" static=\"true\" />\n      <emptyLine />\n      <package name=\"java\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"javax\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"org\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"com\" withSubpackages=\"true\" static=\"false\" />\n      <emptyLine />\n      <package name=\"\" withSubpackages=\"true\" static=\"false\" />\n    </value>\n  </option>\n  <codeStyleSettings language=\"JAVA\">\n    <option name=\"KEEP_BLANK_LINES_IN_CODE\" value=\"1\" />\n    <option name=\"BLANK_LINES_AFTER_CLASS_HEADER\" value=\"1\" />\n  </codeStyleSettings>\n</code_scheme>"
  },
  {
    "path": "README-zh.md",
    "content": "<p align=\"center\">\n  <a href=\"https://github.com/baomidou/mybatis-plus\" target=\"_blank\">\n   <img alt=\"Mybatis-Plus-Logo\" src=\"https://raw.githubusercontent.com/baomidou/logo/master/mybatis-plus-logo-new-mini.png\">\n  </a>\n</p>\n\n<p align=\"center\">\n  为简化开发工作、提高生产率而生\n</p>\n\n<p align=\"center\">\n\n  <a href=\"https://github.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://img.shields.io/github/stars/baomidou/mybatis-plus?style=social\">\n  </a>\n\n  <a href=\"https://gitee.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://gitee.com/baomidou/mybatis-plus/badge/star.svg\">\n  </a>\n\n  <a href=\"https://gitcode.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://gitcode.com/baomidou/mybatis-plus/star/badge.svg\">\n  </a>\n\n  <a href=\"https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*\">\n    <img alt=\"maven\" src=\"https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg?style=flat-square\">\n  </a>\n\n  <a href=\"https://www.apache.org/licenses/LICENSE-2.0\">\n    <img alt=\"code style\" src=\"https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square\">\n  </a>\n</p>\n\n[企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples)\n\n# 简介 | Intro\n\nMybatis 增强工具包 - 只做增强不做改变，简化`CRUD`操作\n\n添加 `微信 wx153666` 备注进 mp 群\n\n> 不允许非法项目使用，后果自负\n\n# 特别用户\n\n<p>\n  <a href=\"https://doc.flowlong.com?from=mp\" target=\"_blank\">\n   <img alt=\"aizuda-Logo\" src=\"https://foruda.gitee.com/images/1715955628416785121/954c16ef_12260.png\" width=\"160px\" height=\"50px\">\n  </a>\n  <a href=\"https://gitee.com/gz-yami/mall4j?from=mp\" target=\"_blank\">\n   <img alt=\"mall4j-Logo\" src=\"https://foruda.gitee.com/images/1716776021837872678/87883b39_12260.gif\" width=\"160px\" height=\"50px\">\n  </a>\n  <a href=\"http://github.crmeb.net/u/MyBatis-Plus\" target=\"_blank\">\n   <img alt=\"crmeb-Logo\" src=\"https://foruda.gitee.com/images/1685339553088166856/b0a6b1a4_12260.gif\" width=\"160px\" height=\"50px\">\n  </a>\n</p>\n\n# 依赖引用\n\n- Latest\n  Version: [![Maven Central](https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg)](https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*)\n    - Maven:\n    - SpringBoot2\n      ```xml\n      <dependency>\n          <groupId>com.baomidou</groupId>\n          <artifactId>mybatis-plus-boot-starter</artifactId>\n          <version>Latest Version</version>\n      </dependency>\n      ```\n    - SpringBoot3\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n    - SpringBoot4 `^3.5.13`\n      ```xml\n      <dependency>\n      <groupId>com.baomidou</groupId>\n      <artifactId>mybatis-plus-spring-boot4-starter</artifactId>\n      <version>Latest Version</version>\n      </dependency>\n      ```\n    - `^3.5.9` 你可能需要额外的引用\n        - jdk11+\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-jsqlparser</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n        - jdk8\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n        - Gradle\n        - SpringBoot2\n          ```groovy\n          compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: 'Latest Version'\n          ```\n        - SpringBoot3\n          ```groovy\n          compile group: 'com.baomidou', name: 'mybatis-plus-spring-boot3-starter', version: 'Latest Version'\n          ```\n\n# 优点 | Advantages\n\n- **无侵入**：Mybatis-Plus 在 Mybatis 的基础上进行扩展，只做增强不做改变，引入 Mybatis-Plus 不会对您现有的 Mybatis\n  构架产生任何影响，而且 MP 支持所有 Mybatis 原生的特性\n- **依赖少**：仅仅依赖 Mybatis 以及 Mybatis-Spring\n- **损耗小**：启动即会自动注入基本CURD，性能基本无损耗，直接面向对象操作\n- **通用CRUD操作**：内置通用 Mapper、通用 Service，仅仅通过少量配置即可实现单表大部分 CRUD 操作，更有强大的条件构造器，满足各类使用需求\n- **多种主键策略**：支持多达4种主键策略（内含分布式唯一ID生成器），可自由配置，完美解决主键问题\n- **支持ActiveRecord**：支持 ActiveRecord 形式调用，实体类只需继承 Model 类即可实现基本 CRUD 操作\n- **支持代码生成**：采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller\n  层代码，支持模板引擎，更有超多自定义配置等您来使用（P.S. 比 Mybatis 官方的 Generator 更加强大！）\n- **支持自定义全局通用操作**：支持全局通用方法注入( Write once, use anywhere )\n- **内置分页插件**：基于Mybatis物理分页，开发者无需关心具体操作，配置好插件之后，写分页等同于写基本List查询\n- **内置性能分析插件**：可输出Sql语句以及其执行时间，建议开发测试时启用该功能，能有效解决慢查询\n- **内置全局拦截插件**：提供全表 delete 、 update 操作智能分析阻断，预防误操作\n\n## 相关链接 | Links\n\n- [文档](https://baomidou.com)\n- [代码生成](https://github.com/baomidou/generator)\n- [功能示例](https://github.com/baomidou/mybatis-plus-samples)\n- [展示](https://github.com/baomidou/awesome-mybatis-plus)\n- [企业版 Mybatis-Mate 高级特性](https://github.com/baomidou/mybatis-mate-examples)\n\n\n# 其他开源项目 | Other Project\n\n- [基于Cookie的SSO中间件 Kisso](https://gitee.com/baomidou/kisso)\n- [Java快速开发框架 SpringWind](https://gitee.com/juapk/SpringWind)\n- [基于Hibernate扩展 Hibernate-Plus](https://gitee.com/baomidou/hibernate-plus)\n- [基于 pac4j-jwt 的快速集成的 web 安全组件 shaun](https://gitee.com/baomidou/shaun)\n\n# 王者荣耀\n\n![MPTrophy](./images/c9bf2ba5_12260.jpeg \"mybatis-plus.jpg\")\n![GitCode](./images/2025-05-21_163231_398.jpg \"2025-05-21_163231_398.jpg\")\n# 期望 | Futures\n\n> 欢迎提出更好的意见，帮助完善 Mybatis-Plus\n\n# 版权 | License\n\n[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n\n![捐赠 mybatis-plus](./images/211207_0acab44e_12260.png \"支持一下mybatis-plus\")\n\n# 关注我 | About Me\n* Github: https://github.com/baomidou/mybatis-plus\n* Gitee: https://gitee.com/baomidou/mybatis-plus\n* GitCode: https://gitcode.com/baomidou/mybatis-plus\n![程序员日记](./images/181933_46d5b802_12260.png \"程序员日记\")\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://github.com/baomidou/mybatis-plus\">\n   <img alt=\"Mybatis-Plus-Logo\" src=\"https://raw.githubusercontent.com/baomidou/logo/master/mybatis-plus-logo-new-mini.png\">\n  </a>\n</p>\n\n<p align=\"center\">\n  Born To Simplify Development\n</p>\n\n<p align=\"center\">\n\n  <a href=\"https://github.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://img.shields.io/github/stars/baomidou/mybatis-plus?style=social\">\n  </a>\n\n  <a href=\"https://gitee.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://gitee.com/baomidou/mybatis-plus/badge/star.svg\">\n  </a>\n\n  <a href=\"https://gitcode.com/baomidou/mybatis-plus\">\n    <img alt=\"code style\" src=\"https://gitcode.com/baomidou/mybatis-plus/star/badge.svg\">\n  </a>\n\n  <a href=\"https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*\">\n    <img alt=\"maven\" src=\"https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg?style=flat-square\">\n  </a>\n\n</p>\n\n[企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples)\n\n添加 `微信 wx153666` 备注进 mp 群\n\n> 不允许非法项目使用，后果自负\n\n# Special user\n\n<p>\n  <a href=\"https://doc.flowlong.com?from=mp\" target=\"_blank\">\n   <img alt=\"aizuda-Logo\" src=\"https://foruda.gitee.com/images/1715955628416785121/954c16ef_12260.png\" width=\"160px\" height=\"50px\">\n  </a>\n  <a href=\"https://gitee.com/gz-yami/mall4j?from=mp\" target=\"_blank\">\n   <img alt=\"mall4j-Logo\" src=\"https://foruda.gitee.com/images/1716776021837872678/87883b39_12260.gif\" width=\"160px\" height=\"50px\">\n  </a>\n</p>\n\n## What is MyBatis-Plus?\n\nMyBatis-Plus is a powerful and enhanced toolkit of MyBatis for simplifying development.\nIt provides efficient, and out-of-the-box features (such as code generation, conditional query builders, pagination plugins...), effectively saving development time\n\n## Links\n\n- [Documentation](https://baomidou.com)\n- [Code Generator](https://github.com/baomidou/generator)\n- [Samples](https://github.com/baomidou/mybatis-plus-samples)\n- [Showcase](https://github.com/baomidou/awesome-mybatis-plus)\n- [企业版 Mybatis-Mate 高级特性](https://gitee.com/baomidou/mybatis-mate-examples)\n\n## Features\n\n- Fully compatible with MyBatis\n- Auto configuration on startup\n- Out-of-the-box interfaces for operate database\n- Powerful and flexible where condition wrapper\n- Multiple strategy to generate primary key\n- Lambda-style API\n- Almighty and highly customizable code generator\n- Automatic paging operation\n- SQL Inject defense\n- Support active record\n- Support pluggable custom interface\n- Build-in many useful extensions\n\n## Getting started\n\n- Add MyBatis-Plus dependency\n    - Latest\n      Version: [![Maven Central](https://img.shields.io/maven-central/v/com.baomidou/mybatis-plus.svg)](https://search.maven.org/search?q=g:com.baomidou%20a:mybatis-*)\n    - Maven:\n    - SpringBoot2\n      ```xml\n      <dependency>\n          <groupId>com.baomidou</groupId>\n          <artifactId>mybatis-plus-boot-starter</artifactId>\n          <version>Latest Version</version>\n      </dependency>\n      ```\n    - SpringBoot3\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-spring-boot3-starter</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n    - SpringBoot4 `^3.5.13`\n      ```xml\n      <dependency>\n      <groupId>com.baomidou</groupId>\n      <artifactId>mybatis-plus-spring-boot4-starter</artifactId>\n      <version>Latest Version</version>\n      </dependency>\n      ```\n    - `^3.5.9` may need additional citations\n        - jdk11+\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-jsqlparser</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n        - jdk8\n      ```xml\n      <dependency>\n        <groupId>com.baomidou</groupId>\n        <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>\n        <version>Latest Version</version>\n      </dependency>\n      ```\n\n    - Gradle\n    - SpringBoot2\n      ```groovy\n      compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: 'Latest Version'\n      ```\n    - SpringBoot3\n      ```groovy\n      compile group: 'com.baomidou', name: 'mybatis-plus-spring-boot3-starter', version: 'Latest Version'\n      ```\n- Modify mapper file extends BaseMapper interface\n\n  ```java\n  public interface UserMapper extends BaseMapper<User> {\n\n  }\n  ```\n\n- Use it\n  ``` java\n  List<User> userList = userMapper.selectList(\n          new QueryWrapper<User>()\n                  .lambda()\n                  .ge(User::getAge, 18)\n  );\n  ```\n  MyBatis-Plus will execute the following SQL\n    ```sql\n    SELECT * FROM user WHERE age >= 18\n    ```\n\n> This showcase is just a small part of MyBatis-Plus features. If you want to learn more, please refer to\n> the [documentation](https://baomidou.com).\n\n## License\n\nMyBatis-Plus is under the Apache 2.0 license. See the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)\nfile for details.\n"
  },
  {
    "path": "build.gradle",
    "content": "import java.time.LocalDateTime\n\nallprojects {\n    group APP_GROUP\n    version APP_VERSION\n}\n\next {\n    configuration = [\n        javaVersion = JavaVersion.VERSION_21\n    ]\n\n    libraries = [\n        mybatisVersion = '3.5.19',\n        mybatisSpringVersion = '2.1.2',\n        mybatisSpringBootStarterVersion = '2.3.2',\n        springVersion = '5.3.39',\n        springBootVersion = '2.7.18',\n        springBoot3Version = '3.5.9',\n        springBoot4Version = '4.0.1',\n        springCloudVersion = '3.1.8',\n        junitVersion = '5.14.1',\n    ]\n\n    lib = [\n        \"kotlin-reflect\"             : \"org.jetbrains.kotlin:kotlin-reflect:2.1.0\",\n        \"kotlin-stdlib-jdk8\"         : \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0\",\n        \"mybatis\"                    : \"org.mybatis:mybatis:${mybatisVersion}\",\n        \"mybatis-spring\"             : \"org.mybatis:mybatis-spring:${mybatisSpringVersion}\",\n        \"mybatis-thymeleaf\"          : \"org.mybatis.scripting:mybatis-thymeleaf:1.0.4\",\n        \"mybatis-freemarker\"         : \"org.mybatis.scripting:mybatis-freemarker:1.3.0\",\n        \"mybatis-velocity\"           : \"org.mybatis.scripting:mybatis-velocity:2.1.2\",\n        \"spring-context-support\"     : \"org.springframework:spring-context-support:${springVersion}\",\n        \"spring-jdbc\"                : \"org.springframework:spring-jdbc:${springVersion}\",\n        \"spring-tx\"                  : \"org.springframework:spring-tx:${springVersion}\",\n        \"spring-web\"                 : \"org.springframework:spring-web:${springVersion}\",\n        \"spring-aop\"                 : \"org.springframework:spring-aop:${springVersion}\",\n        \"cglib\"                      : \"cglib:cglib:3.3.0\",\n        \"imadcn\"                     : \"com.imadcn.framework:idworker:1.6.0\",\n        \"spring-cloud-commons\"       : \"org.springframework.cloud:spring-cloud-commons:${springCloudVersion}\",\n\n        \"aspectjweaver\"              : \"org.aspectj:aspectjweaver:1.9.22.1\",\n        \"slf4j-api\"                  : \"org.slf4j:slf4j-api:1.7.36\",\n        //copy\n        \"mybatis-spring-boot-starter\": \"org.mybatis.spring.boot:mybatis-spring-boot-starter:${mybatisSpringBootStarterVersion}\",\n        //test\n        \"spring-test\"                : \"org.springframework:spring-test:${springVersion}\",\n        \"assertj-core\"               : \"org.assertj:assertj-core:3.27.6\",\n        \"junit-jupiter\"              : \"org.junit.jupiter:junit-jupiter:${junitVersion}\",\n        \"fastjson\"                   : \"com.alibaba:fastjson:2.0.60\",\n        \"jackson\"                    : \"com.fasterxml.jackson.core:jackson-databind:2.20.1\",\n        \"jackson3\"                   : \"tools.jackson.core:jackson-databind:3.0.3\",\n        \"gson\"                       : \"com.google.code.gson:gson:2.13.2\",\n        \"lagarto\"                    : \"org.jodd:jodd-lagarto:6.0.6\",\n        //datasource\n        \"p6spy\"                      : \"p6spy:p6spy:3.9.1\",\n        \"sqlserver\"                  : \"com.microsoft.sqlserver:sqljdbc4:4.0\",\n        \"postgresql\"                 : \"org.postgresql:postgresql:42.7.8\",\n        \"oracle\"                     : \"com.oracle.database.jdbc:ojdbc8:23.7.0.25.01\",\n        \"dm\"                         : fileTree(dir: 'libs', includes: [\"jdbcDriver-18.jar\"]),\n        \"h2\"                         : \"com.h2database:h2:2.4.240\",\n        \"mysql\"                      : \"com.mysql:mysql-connector-j:9.5.0\",\n        \"sqlite\"                     : \"org.xerial:sqlite-jdbc:3.51.1.0\",\n        \"firebird\"                   : \"org.firebirdsql.jdbc:jaybird:5.0.10.java8\",\n        \"gaussdb\"                    : \"com.huaweicloud.gaussdb:gaussdbjdbc:506.0.0.b058\",\n        //cache\n        \"mybatis-caffeine\"           : \"org.mybatis.caches:mybatis-caffeine:1.2.0\",\n        //code generator\n        \"velocity\"            : \"org.apache.velocity:velocity-engine-core:2.4.1\",\n        \"freemarker\"          : \"org.freemarker:freemarker:2.3.33\",\n        \"beetl\"               : \"com.ibeetl:beetl:3.17.0.RELEASE\",\n        \"swagger-annotations\" : \"io.swagger:swagger-annotations:1.6.14\",\n        \"enjoy\"               : \"com.jfinal:enjoy:5.2.2\",\n        \"logback-classic\"     : \"ch.qos.logback:logback-classic:1.5.15\",\n    ]\n}\n\ndescription = \"Mybatis 增强工具包 - 只做增强不做改变，简化CRUD操作\"\n\nsubprojects {\n    apply plugin: 'java-library'\n    apply plugin: 'signing'\n    apply plugin: 'maven-publish'\n    apply plugin: 'tech.yanand.maven-central-publish'\n    apply plugin: \"io.freefair.lombok\"\n\n    sourceCompatibility = \"${javaVersion}\"\n    targetCompatibility = \"${javaVersion}\"\n\n    lombok {\n        version = \"1.18.32\"\n    }\n\n    compileJava {\n        options.release = 8\n    }\n\n    repositories {\n        mavenLocal()\n        maven { url \"https://maven.aliyun.com/repository/public\" }\n        maven { url \"https://oss.sonatype.org/content/repositories/snapshots/\" }\n        mavenCentral()\n    }\n\n    dependencies {\n        testImplementation \"${lib[\"assertj-core\"]}\"\n        testImplementation \"${lib[\"junit-jupiter\"]}\"\n        testImplementation \"org.mockito:mockito-junit-jupiter:5.21.0\"\n        testImplementation \"${lib[\"lagarto\"]}\"\n        testImplementation(\"org.junit.platform:junit-platform-engine:6.0.1\")\n        testImplementation(\"org.junit.jupiter:junit-jupiter-engine:6.0.1\")\n        testImplementation(\"org.junit.platform:junit-platform-launcher:6.0.1\")\n    }\n\n    tasks.withType(JavaCompile) {\n        options.encoding = 'UTF-8'\n        options.warnings = false\n        options.deprecation = true\n        options.compilerArgs += [\"-parameters\"]\n    }\n\n    tasks.withType(GenerateModuleMetadata) {\n        enabled = false\n    }\n\n    jar {\n        into(\"META-INF/\") {\n            from rootProject.file(\"LICENSE\")\n        }\n        into(\"META-INF/maven/$project.group/$project.name\") {\n            from { generatePomFileForMavenJavaPublication }\n            rename \".*\", \"pom.xml\"\n        }\n        afterEvaluate {\n            manifest {\n                attributes 'Implementation-Title': archiveBaseName\n                attributes 'Implementation-Version': archiveVersion\n                attributes 'Built-Gradle': gradle.gradleVersion\n                attributes 'Bundle-DocURL': 'https://baomidou.com/'\n                attributes 'Build-OS': System.getProperty(\"os.name\")\n                attributes 'Built-By': System.getProperty(\"user.name\")\n                attributes 'Build-Jdk': System.getProperty(\"java.version\")\n                attributes 'Build-Timestamp': LocalDateTime.now().format(\"yyyy-MM-dd HH:mm:ss\")\n                attributes 'Automatic-Module-Name': \"${project.group}.${project.name.replaceAll(\"-\", \".\")}\"\n            }\n        }\n    }\n\n    //noinspection GroovyAssignabilityCheck\n    task sourcesJar(type: Jar) {\n        archiveClassifier = 'sources'\n        from sourceSets.main.allSource\n    }\n\n    javadoc {\n        afterEvaluate {\n            configure(options) {\n                encoding \"UTF-8\"\n                charSet 'UTF-8'\n                author true\n                version true\n                failOnError false\n                links \"http://docs.oracle.com/javase/8/docs/api\"\n            }\n        }\n    }\n\n    test {\n        dependsOn(\"cleanTest\", \"generatePomFileForMavenJavaPublication\")\n        useJUnitPlatform()\n        exclude(\"**/phoenix/**\")\n        exclude(\"**/postgresql/**\")\n        exclude(\"**/gaussdb/**\")\n//        exclude(\"**/generator/**\")\n    }\n\n    task cleanBuildDir(type: Delete) {\n        delete \"${projectDir}/out\"\n    }\n    tasks.clean.dependsOn(cleanBuildDir)\n\n    task javadocJar(type: Jar) {\n        archiveClassifier = 'javadoc'\n        from javadoc\n    }\n\n    tasks.whenTaskAdded { task ->\n        if (task.name.contains('signMavenJavaPublication')) {\n            task.enabled = new File(project.property('signing.secretKeyRingFile') as String).isFile()\n        }\n    }\n\n    publishing {\n        repositories {\n            maven {\n                name = \"Local\"\n                url = layout.buildDirectory.dir('repos/bundles')\n            }\n            maven {\n                def userName = System.getProperty(\"un\")\n                def passWord = System.getProperty(\"ps\")\n                name = \"snapshots\"\n                url = \"https://central.sonatype.com/repository/maven-snapshots/\"\n                credentials {\n                    username userName\n                    password passWord\n                }\n            }\n        }\n\n        // use example : ./gradlew clean build publishMavenJavaPublicationToLocalRepository publishToMavenCentralPortal -DauthToken='xxxxxx' -x test\n        mavenCentral {\n            repoDir = layout.buildDirectory.dir('repos/bundles')\n            // Base64 encoded of \"username:password\"\n            authToken = System.getProperty(\"authToken\")\n            // 默认自动发布 AUTOMATIC\n            publishingType = 'USER_MANAGED'\n        }\n\n        publications {\n\n            mavenJava(MavenPublication) {\n                from components.java\n\n                artifact sourcesJar\n                artifact javadocJar\n\n                pom {\n                    name = 'mybatis-plus'\n                    packaging 'jar'\n                    description = 'An enhanced toolkit of Mybatis to simplify development.'\n                    url = 'https://github.com/baomidou/mybatis-plus'\n\n                    scm {\n                        connection = 'scm:git@github.com:Codearte/gradle-nexus-staging-plugin.git'\n                        developerConnection = 'scm:git@github.com:Codearte/gradle-nexus-staging-plugin.git'\n                        url = 'https://github.com/baomidou/mybatis-plus'\n                    }\n\n                    licenses {\n                        license {\n                            name = 'The Apache License, Version 2.0'\n                            url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                        }\n                    }\n\n                    developers {\n                        developer {\n                            id = 'baomidou'\n                            name = 'hubin'\n                            email = 'jobob@qq.com'\n                        }\n                    }\n\n                    withXml {\n                        def root = asNode()\n                        root.dependencies.'*'.findAll {\n                            def d = it\n                            d.scope.text() == 'runtime' && project.configurations.findByName(\"implementation\").allDependencies.find { dep ->\n                                dep.name == it.artifactId.text()\n                            }.each() {\n                                d.scope*.value = 'compile'\n                                d.appendNode('optional', true)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        signing {\n            sign publishing.publications.mavenJava\n        }\n    }\n}\n"
  },
  {
    "path": "changelog-temp.md",
    "content": "- feat: 支持配置加密在环境变量中使用\n"
  },
  {
    "path": "gradle.properties",
    "content": "APP_VERSION=3.5.17-SNAPSHOT\nAPP_GROUP=com.baomidou\nsigning.keyId=1FD337F9\nsigning.password=243194995\nsigning.secretKeyRingFile=/home/nieqiurong/signing.gpg\n#signing.secretKeyRingFile=/Users/ming/Documents/signing.gpg\n"
  },
  {
    "path": "license.txt",
    "content": "Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "mybatis-plus/build.gradle",
    "content": "apply plugin: 'kotlin'\n\ncompileKotlin{\n    kotlinOptions.jvmTarget = \"1.8\"\n}\n\ncompileTestKotlin {\n    kotlinOptions {\n        freeCompilerArgs = ['-Xjvm-default=all']\n    }\n}\n\nconfigurations {\n    testImplementation.exclude module: 'mybatis-plus-jsqlparser-4.9'\n}\n\ndependencies {\n    api project(\":mybatis-plus-core\")\n    api project(\":mybatis-plus-annotation\")\n    api project(\":mybatis-plus-spring\")\n\n    api \"${lib.mybatis}\"\n\n    implementation \"${lib.\"mybatis-spring\"}\"\n    implementation \"${lib.\"kotlin-stdlib-jdk8\"}\"\n    implementation project(\":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-4.9\")\n\n    testImplementation \"${lib.'spring-web'}\"\n\n    testImplementation \"${lib.'spring-test'}\"\n    testImplementation \"${lib.'spring-aop'}\"\n    testImplementation \"${lib.'aspectjweaver'}\"\n    testImplementation \"${lib.\"jackson\"}\"\n    testImplementation \"${lib.\"fastjson\"}\"\n    testImplementation \"${lib.\"gson\"}\"\n\n    testImplementation \"${lib.h2}\"\n    testImplementation \"${lib.sqlserver}\"\n    testImplementation \"${lib.postgresql}\"\n    testImplementation \"${lib.oracle}\"\n    testImplementation \"${lib.mysql}\"\n    testImplementation \"${lib.'spring-context-support'}\"\n    testImplementation \"${lib.'spring-jdbc'}\"\n    testImplementation \"${lib.'slf4j-api'}\"\n    testImplementation \"${lib.'logback-classic'}\"\n    testImplementation \"${lib.cglib}\"\n    testImplementation \"${lib.postgresql}\"\n    testImplementation \"${lib.gaussdb}\"\n    testImplementation project(\":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser\")\n//    testCompile ('org.apache.phoenix:phoenix-core:5.0.0-HBase-2.0')\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/BaseDbTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;\nimport com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.logging.slf4j.Slf4jImpl;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.transaction.managed.ManagedTransactionFactory;\nimport org.apache.ibatis.type.TypeReference;\nimport org.h2.Driver;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic abstract class BaseDbTest<T> extends TypeReference<T> {\n\n    protected SqlSessionFactory sqlSessionFactory;\n    protected Class<T> mapper;\n    protected JdbcTemplate jdbcTemplate;\n\n    @SuppressWarnings(\"unchecked\")\n    public BaseDbTest() {\n        DataSource ds = dataSource();\n        List<String> tableSql = tableSql();\n        String tableDataSql = tableDataSql();\n        String mapperXml = mapperXml();\n        GlobalConfig globalConfig = globalConfig();\n        List<Interceptor> interceptors = interceptors();\n        Consumer<Configuration> consumer = consumer();\n        mapper = (Class<T>) getRawType();\n\n        jdbcTemplate = new JdbcTemplate(ds);\n        if (CollectionUtils.isNotEmpty(tableSql)) {\n            for (String sql : tableSql) {\n                if (StringUtils.isNotBlank(sql)) {\n                    jdbcTemplate.execute(sql);\n                }\n            }\n        }\n\n        if (StringUtils.isNotBlank(tableDataSql)) {\n            jdbcTemplate.execute(tableDataSql);\n        }\n        MybatisSqlSessionFactoryBuilder builder = new MybatisSqlSessionFactoryBuilder();\n        Environment environment = new Environment(\"test\", new ManagedTransactionFactory(), ds);\n        MybatisConfiguration configuration = new MybatisConfiguration(environment);\n        if (consumer != null) {\n            consumer.accept(configuration);\n        }\n        GlobalConfigUtils.setGlobalConfig(configuration, globalConfig);\n        configuration.setLogImpl(Slf4jImpl.class);\n        if (StringUtils.isNotBlank(mapperXml)) {\n            try {\n                InputStream inputStream = Resources.getResourceAsStream(mapperXml);\n                MybatisXMLMapperBuilder xmlMapperBuilder = new MybatisXMLMapperBuilder(inputStream,\n                    configuration, mapperXml, configuration.getSqlFragments());\n                xmlMapperBuilder.parse();\n            } catch (IOException e) {\n                throw ExceptionUtils.mpe(e);\n            }\n        }\n        configuration.addMapper(mapper);\n        otherMapper().forEach(configuration::addMapper);\n\n        if (CollectionUtils.isNotEmpty(interceptors)) {\n            interceptors.forEach(configuration::addInterceptor);\n        }\n        sqlSessionFactory = builder.build(configuration);\n    }\n\n    private DataSource dataSource() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    protected SqlSession sqlSession(ExecutorType type) {\n        return sqlSessionFactory.openSession(type);\n    }\n\n    protected void doTest(Consumer<T> consumer) {\n        try (SqlSession sqlSession = sqlSession(null)) {\n            doTest(sqlSession, consumer);\n        }\n    }\n\n    protected void doTestAutoCommit(Consumer<T> consumer) {\n        try (SqlSession sqlSession = sqlSession(null)) {\n            doTestAutoCommit(sqlSession, consumer);\n        }\n    }\n\n    protected void doTest(SqlSession sqlSession, Consumer<T> consumer) {\n        doMapper(sqlSession, false, consumer);\n    }\n\n    protected void doTestAutoCommit(SqlSession sqlSession, Consumer<T> consumer) {\n        doMapper(sqlSession, true, consumer);\n    }\n\n    protected void doMapper(SqlSession sqlSession, boolean commit, Consumer<T> consumer) {\n        T t = sqlSession.getMapper(mapper);\n        consumer.accept(t);\n        if (commit) {\n            sqlSession.commit();\n        } else {\n            sqlSession.rollback();\n        }\n    }\n\n    protected List<String> tableSql() {\n        return null;\n    }\n\n    protected String tableDataSql() {\n        return null;\n    }\n\n    protected String mapperXml() {\n        return null;\n    }\n\n    protected List<Interceptor> interceptors() {\n        return null;\n    }\n\n    protected GlobalConfig globalConfig() {\n        return GlobalConfigUtils.defaults();\n    }\n\n    protected Consumer<Configuration> consumer() {\n        return null;\n    }\n\n    protected List<Class<?>> otherMapper() {\n        return Collections.emptyList();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/DbTypeTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\n/**\n * @author nieqiuqiu\n */\nclass DbTypeTest {\n\n    @Test\n    void testGetDbType() {\n        Assertions.assertEquals(DbType.MYSQL, DbType.getDbType(\"mysql\"));\n        Assertions.assertEquals(DbType.MYSQL, DbType.getDbType(\"Mysql\"));\n        Assertions.assertEquals(DbType.OTHER, DbType.getDbType(\"other\"));\n        Assertions.assertEquals(DbType.OTHER, DbType.getDbType(\"unknown\"));\n    }\n\n    @Test\n    void testGaussDb(){\n        Assertions.assertEquals(DbType.GAUSS, DbType.getDbType(\"gauss\"));\n        Assertions.assertEquals(DbType.GAUSS, DbType.getDbType(\"Gauss\"));\n        Assertions.assertEquals(DbType.GAUSS_DB, DbType.getDbType(\"gaussdb\"));\n        Assertions.assertEquals(DbType.GAUSS_DB, DbType.getDbType(\"GaussDB\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/MybatisTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport org.apache.ibatis.exceptions.PersistenceException;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.jdbc.ScriptRunner;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.TypeHandlerRegistry;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\n\n/**\n * 原生Mybatis测试\n *\n * @author nieqiurong 2019/2/27.\n */\n@ExtendWith(MockitoExtension.class)\nclass MybatisTest {\n\n    private static SqlSessionFactory sqlSessionFactory;\n\n\n    @BeforeAll\n    public static void init() throws IOException, SQLException {\n        Reader reader = Resources.getResourceAsReader(\"mybatis-config.xml\");\n        sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(reader);\n        DataSource dataSource = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();\n        Configuration configuration = sqlSessionFactory.getConfiguration();\n        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();\n        /*\n         *  如果是将defaultEnumTypeHandler设置成MP的处理器,\n         *  请自行注册处理非MP枚举处理类的原生枚举类型\n         */\n        typeHandlerRegistry.register(AgeEnum.class, MybatisEnumTypeHandler.class);     //这里我举起了个栗子\n        Connection connection = dataSource.getConnection();\n        ScriptRunner scriptRunner = new ScriptRunner(connection);\n        scriptRunner.runScript(Resources.getResourceAsReader(\"h2/user.ddl.sql\"));\n    }\n\n\n    @Test\n    void test() {\n        try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {\n            H2UserMapper mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertEquals(1, mapper.myInsertWithNameVersion(\"test\", 2));\n            Assertions.assertEquals(1, mapper.insert(new H2User(\"test\")));\n            Assertions.assertEquals(2, mapper.selectCount(new QueryWrapper<H2User>().lambda().eq(H2User::getName, \"test\")));\n            Assertions.assertEquals(2, mapper.delete(new QueryWrapper<H2User>().lambda().eq(H2User::getName, \"test\")));\n            H2User h2User = new H2User(66L, \"66666\", AgeEnum.THREE, 666);\n            Assertions.assertEquals(1, mapper.insert(h2User));\n            h2User.setName(\"7777777777\");\n            H2User user = mapper.selectById(66L);\n            Assertions.assertNotNull(user);\n            Assertions.assertEquals(AgeEnum.THREE, user.getAge());\n            Assertions.assertNotNull(user.getTestType());\n            Assertions.assertEquals(1, mapper.updateById(new H2User(66L, \"777777\")));\n            Assertions.assertEquals(1, mapper.deleteById(66L));\n            Assertions.assertNull(mapper.selectById(66L));\n        }\n    }\n\n    @Test\n    void testBatchAutoCommitFalse() {\n        var userList = List.of(new H2User(2000L, \"测试\"), new H2User(2001L, \"测试\"));\n        MybatisBatchUtils.execute(sqlSessionFactory, userList, H2UserMapper.class.getName() + \".insert\");\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            for (H2User u : userList) {\n                Assertions.assertNotNull(mapper.selectById(u.getTestId()));\n            }\n        }\n    }\n\n    @Test\n    void testBatchAutoCommitFalseOnException1() {\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory,\n            List.of(new H2User(1000L, \"测试\"), new H2User(1000L, \"测试\")), H2UserMapper.class.getName() + \".insert\"));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNull(mapper.selectById(1000L));\n        }\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory,\n            List.of(new H2User(1001L, \"测试\"), new H2User(1001L, \"测试\")),\n            H2UserMapper.class.getName() + \".insert\", 1));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNotNull(mapper.selectById(1001L));\n        }\n    }\n\n    @Test\n    void testBatchAutoCommitFalseOnException2() {\n        List<H2User> userList = List.of(new H2User(1010L, \"测试\"), new H2User(1011L, \"测试\"));\n        Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory, userList, H2UserMapper.class.getName() + \".insert\", parameter -> {\n            if (parameter.getTestId() == 1011L) {\n                throw new RuntimeException(\"出异常了\");\n            }\n            return parameter;\n        }));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNull(mapper.selectById(1010L));\n        }\n    }\n\n\n    @Test\n    void testBatchAutoCommitTrue() {\n        var userList = List.of(new H2User(3000L, \"测试\"), new H2User(3001L, \"测试\"));\n        MybatisBatchUtils.execute(sqlSessionFactory, userList, true, H2UserMapper.class.getName() + \".insert\");\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            for (H2User u : userList) {\n                Assertions.assertNotNull(mapper.selectById(u.getTestId()));\n            }\n        }\n    }\n\n    @Test\n    void testBatchAutoCommitTrueOnException1() {\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory,\n            List.of(new H2User(4000L, \"测试\"), new H2User(4000L, \"测试\")), true, H2UserMapper.class.getName() + \".insert\"));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNotNull(mapper.selectById(4000L));\n        }\n        Assertions.assertThrowsExactly(PersistenceException.class,\n            () -> MybatisBatchUtils.execute(sqlSessionFactory, List.of(new H2User(4020L, \"测试\"), new H2User(4020L, \"测试\"), 1),\n                true, H2UserMapper.class.getName() + \".insert\"));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNotNull(mapper.selectById(4020L));\n        }\n    }\n\n    @Test\n    void testBatchAutoCommitTrueOnException2() {\n        Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory,\n            List.of(new H2User(4010L, \"测试\"), new H2User(4011L, \"测试\")), true, H2UserMapper.class.getName() + \".insert\", parameter -> {\n            if (parameter.getTestId() == 4011L) {\n                throw new RuntimeException(\"出异常了\");\n            }\n            return parameter;\n        }));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNull(mapper.selectById(4010L));\n            Assertions.assertNull(mapper.selectById(4011L));\n        }\n        Assertions.assertThrowsExactly(RuntimeException.class, () -> MybatisBatchUtils.execute(sqlSessionFactory,\n            List.of(new H2User(4015L, \"测试\"), new H2User(4016L, \"测试\")), true,\n            H2UserMapper.class.getName() + \".insert\", parameter -> {\n            if (parameter.getTestId() == 4016L) {\n                throw new RuntimeException(\"出异常了\");\n            }\n            return parameter;\n        },1));\n        try (var sqlSession = sqlSessionFactory.openSession()) {\n            var mapper = sqlSession.getMapper(H2UserMapper.class);\n            Assertions.assertNotNull(mapper.selectById(4015L));\n            Assertions.assertNull(mapper.selectById(4016L));\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/OgnlTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport lombok.Data;\nimport org.apache.ibatis.scripting.xmltags.OgnlCache;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author nieqiuqiu\n */\nclass OgnlTest {\n\n    @Data\n    private static class Bean {\n        private String name;\n        private Map<String, Object> properties;\n    }\n\n    /**\n     * size keys keySet values isEmpty 这五个key值需要注意一下.\n     *\n     * @see org.apache.ibatis.ognl.MapPropertyAccessor#getProperty\n     */\n    @Test\n    void test() {\n        Map<String, Object> propertiesMap = new HashMap<>();\n        propertiesMap.put(\"color\", \"yellow\");\n        propertiesMap.put(\"size\", \"xxxL\");\n        Assertions.assertEquals(\"yellow\", OgnlCache.getValue(\"color\", propertiesMap));\n        Assertions.assertEquals(2, OgnlCache.getValue(\"size\", propertiesMap));\n        Assertions.assertFalse((Boolean) OgnlCache.getValue(\"isEmpty\", propertiesMap));\n        Assertions.assertNull(OgnlCache.getValue(\"['isEmpty']\", propertiesMap));\n        Assertions.assertEquals(\"xxxL\", OgnlCache.getValue(\"['size']\", propertiesMap));\n        Assertions.assertEquals(\"yellow\", OgnlCache.getValue(\"['color']\", propertiesMap));\n        Bean bean = new Bean();\n        bean.setName(\"靓仔\");\n        bean.setProperties(propertiesMap);\n        Assertions.assertEquals(\"靓仔\", OgnlCache.getValue(\"name\", bean));\n        Assertions.assertEquals(\"靓仔\", OgnlCache.getValue(\"['name']\", bean));\n        Assertions.assertEquals(2, OgnlCache.getValue(\"properties.size\", bean));\n        Assertions.assertEquals(\"xxxL\", OgnlCache.getValue(\"properties['size']\", bean));\n        Assertions.assertEquals(\"yellow\", OgnlCache.getValue(\"properties.color\", bean));\n        Assertions.assertEquals(\"yellow\", OgnlCache.getValue(\"properties['color']\", bean));\n        Assertions.assertFalse((Boolean) OgnlCache.getValue(\"properties.isEmpty\", bean));\n        Assertions.assertNull(OgnlCache.getValue(\"properties['isEmpty']\", bean));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/PageTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjson2.JSONB;\nimport com.alibaba.fastjson2.JSONReader;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.gson.Gson;\nimport org.apache.ibatis.reflection.property.PropertyCopier;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.cglib.beans.BeanCopier;\n\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author nieqiurong 2020/3/20.\n */\nclass PageTest {\n\n    private final ObjectMapper objectMapper = new ObjectMapper();\n\n    private final Gson gson = new Gson();\n\n    @Test\n    @EnabledOnJre(JRE.JAVA_8)\n    void testCopy() {\n        Page<?> page1 = new Page<>(2, 10, 100, false);\n        page1.setOptimizeCountSql(false);\n        page1.setOrders(Collections.singletonList(OrderItem.asc(\"test\")));\n\n        Page<?> page2 = new Page<>();\n        PropertyCopier.copyBeanProperties(Page.class, page1, page2);\n        Assertions.assertEquals(page1.getCurrent(), page2.getCurrent());\n        Assertions.assertEquals(page1.getTotal(), page2.getTotal());\n        Assertions.assertEquals(page1.getSize(), page2.getSize());\n        Assertions.assertEquals(page1.optimizeCountSql(), page2.optimizeCountSql());\n        Assertions.assertEquals(page1.searchCount(), page2.searchCount());\n        Assertions.assertEquals(page1.orders().size(), page2.orders().size());\n\n        Page<?> page3 = new Page<>();\n        BeanUtils.copyProperties(page1, page3);\n        Assertions.assertEquals(page1.getCurrent(), page3.getCurrent());\n        Assertions.assertEquals(page1.getTotal(), page3.getTotal());\n        Assertions.assertEquals(page1.getSize(), page3.getSize());\n        Assertions.assertEquals(page1.optimizeCountSql(), page3.optimizeCountSql());\n        Assertions.assertEquals(page1.searchCount(), page3.searchCount());\n        Assertions.assertEquals(page1.orders().size(), page3.orders().size());\n\n        Page<?> page4 = new Page<>();\n        BeanCopier.create(page1.getClass(), page4.getClass(), false).copy(page1, page4, null);\n        //链式的set方法会导致属性BeanCopier的拷贝方法失败  https://github.com/cglib/cglib/issues/108.\n        Assertions.assertTrue(page4.optimizeCountSql());\n        Assertions.assertTrue(page4.searchCount());\n        Assertions.assertEquals(1, page4.getCurrent());\n        Assertions.assertEquals(1, page4.orders().size());\n        Assertions.assertEquals(10, page4.getSize());\n    }\n\n    @Test\n    void testPageToJson() throws JsonProcessingException {\n        var page = new Page<>(1, 10, 2000);\n        page.setOrders(List.of(OrderItem.asc(\"a\")));\n        //page无法序列化排序等其他属性 {\"records\":[],\"total\":2000,\"size\":10,\"current\":1,\"pages\":200}\n        assertPage(page);\n    }\n\n\n    @Test\n    void testPageDtoToJson() throws JsonProcessingException {\n        var page = new PageDTO<>(1, 10, 100);\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setOptimizeCountSql(false);\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setSearchCount(false);\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setOptimizeJoinOfCountSql(false);\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setRecords(List.of(\"1\", \"2\", \"3\"));\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setRecords(List.of(\"1\", \"2\", \"3\"));\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setMaxLimit(1000L);\n        page.setRecords(List.of(\"1\", \"2\", \"3\"));\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setRecords(List.of(\"1\", \"2\", \"3\"));\n        page.setCountId(\"123\");\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setOrders(OrderItem.descs(\"a\",\"b\"));\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setOrders(OrderItem.ascs(\"a\",\"b\"));\n        assertPageDto(page);\n\n        page = new PageDTO<>(1, 10, 100);\n        page.setRecords(List.of(\"1\", \"2\", \"3\"));\n        page.setOrders(OrderItem.ascs(\"a\",\"b\"));\n        assertPageDto(page);\n\n    }\n\n    private void assertPage(Page<?> source) throws JsonProcessingException {\n        toConvert(source, Page.class).forEach(target -> {\n            Assertions.assertEquals(source.getCurrent(), target.getCurrent());\n            Assertions.assertEquals(source.getTotal(), target.getTotal());\n            Assertions.assertEquals(source.getSize(), target.getSize());\n            Assertions.assertEquals(source.countId(), target.countId());\n            Assertions.assertEquals(source.getRecords().size(), target.getRecords().size());\n            Assertions.assertEquals(source.getPages(), target.getPages());\n        });\n    }\n\n    private <T extends IPage<?>> List<T> toConvert(T source, Class<T> tClass) throws JsonProcessingException {\n        return List.of(\n            objectMapper.readValue(objectMapper.writeValueAsString(source), tClass),\n            gson.fromJson(gson.toJson(source), tClass),\n            JSON.parseObject(JSON.toJSONString(source), tClass),\n            // dubbo 反序列化下出现问题  https://github.com/alibaba/fastjson2/issues/2734\n            JSONB.parseObject(JSONB.toBytes(source), tClass,\n                JSONReader.Feature.FieldBased\n            ),\n            com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(source), tClass)\n        );\n    }\n\n    private void assertPageDto(PageDTO<?> source) throws JsonProcessingException {\n        toConvert(source, PageDTO.class).forEach(target -> {\n            Assertions.assertEquals(source.toString(), target.toString());\n            Assertions.assertEquals(source.getCurrent(), target.getCurrent());\n            Assertions.assertEquals(source.getTotal(), target.getTotal());\n            Assertions.assertEquals(source.getSize(), target.getSize());\n            Assertions.assertEquals(source.countId(), target.getCountId());\n            Assertions.assertEquals(source.countId(), target.countId());\n            Assertions.assertEquals(source.searchCount(), target.isSearchCount());\n            Assertions.assertEquals(source.searchCount(), target.searchCount());\n            Assertions.assertEquals(source.optimizeCountSql(), target.isOptimizeCountSql());\n            Assertions.assertEquals(source.optimizeCountSql(), target.optimizeCountSql());\n            Assertions.assertEquals(source.optimizeJoinOfCountSql(), target.optimizeJoinOfCountSql());\n            Assertions.assertEquals(source.optimizeJoinOfCountSql(), target.isOptimizeJoinOfCountSql());\n            Assertions.assertEquals(source.getRecords().size(), target.getRecords().size());\n            Assertions.assertEquals(source.maxLimit(), target.getMaxLimit());\n            Assertions.assertEquals(source.maxLimit(), target.maxLimit());\n            Assertions.assertEquals(source.getOrders().size(), target.getOrders().size());\n            Assertions.assertEquals(source.getOrders().size(), target.orders().size());\n            Assertions.assertEquals(source.getPages(), target.getPages());\n        });\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/AutoResultMapTest.java",
    "content": "package com.baomidou.mybatisplus.test.autoresultmap;\n\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class AutoResultMapTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        doTestAutoCommit(m -> m.insert(new Entity().setName(\"老王\").setGg(new Entity.Gg(\"老王\"))));\n        doTest(m -> {\n            Entity entity = m.selectOne(null);\n            assertThat(entity).as(\"插入正常\").isNotNull();\n            assertThat(entity.getName()).as(\"名称不一致正常\").isNotNull();\n            assertThat(entity.getGg()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getGg().getName()).as(\"是老王\").isEqualTo(\"老王\");\n        });\n        doTest(m -> {\n            Entity entity = new Entity().setName(\"老王\");\n            m.selectOne(Wrappers.lambdaQuery(entity).ne(Entity::getId, 1));\n        });\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"x_name VARCHAR(20) NOT NULL,\\n\" +\n                \"gg VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.autoresultmap;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\n@Accessors(chain = true)\n@TableName(autoResultMap = true)\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    @TableField(\"x_name\")\n    private String name;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Gg gg;\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Gg {\n        private String name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/autoresultmap/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.autoresultmap;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/BatchTest.java",
    "content": "package com.baomidou.mybatisplus.test.batch;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.executor.BatchExecutor;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2021-01-06\n */\nclass BatchTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void save() {\n        doTestAutoCommit(sqlSession(ExecutorType.BATCH), i -> {\n            int i1 = i.insert(new Entity(\"老王\"));\n            assertThat(i1).isEqualTo(BatchExecutor.BATCH_UPDATE_RETURN_VALUE);\n            int i2 = i.insert(new Entity(\"老李\"));\n            assertThat(i2).isEqualTo(BatchExecutor.BATCH_UPDATE_RETURN_VALUE);\n        });\n\n        doTest(i -> {\n            assertThat(i.selectCount(null)).isEqualTo(2);\n        });\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.batch;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\n@NoArgsConstructor\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    private String name;\n\n    public Entity(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/batch/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.batch;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCache.java",
    "content": "package com.baomidou.mybatisplus.test.cache.page;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class PageCache implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n    private Long id;\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCacheMapper.java",
    "content": "package com.baomidou.mybatisplus.test.cache.page;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.apache.ibatis.annotations.CacheNamespace;\nimport org.apache.ibatis.annotations.ResultType;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.session.ResultHandler;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@CacheNamespace\npublic interface PageCacheMapper extends BaseMapper<PageCache> {\n\n    @Select(\"<script>select * from page_cache where <if test=\\\"tj.name\\\">name is not null</if></script>\")\n    Page<PageCache> otherPage(Page<?> page, PageCacheTest.Tj tj);\n\n    @ResultType(PageCache.class)\n    @Select(\"<script>select * from page_cache where <if test=\\\"tj.name\\\">name is not null</if></script>\")\n    void otherPageHandler(Page<?> page, PageCacheTest.Tj tj, ResultHandler handler);\n\n    @Select(\"<script>select count(0) from page_cache where <if test=\\\"tj.name\\\">name is not null</if></script>\")\n    Long otherCount(PageCacheTest.Tj tj);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/page/PageCacheTest.java",
    "content": "package com.baomidou.mybatisplus.test.cache.page;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport lombok.Data;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.executor.result.DefaultResultHandler;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\nclass PageCacheTest extends BaseDbTest<PageCacheMapper> {\n\n    @Test\n    void page() {\n        Cache cache = sqlSessionFactory.getConfiguration().getCache(PageCacheMapper.class.getName());\n        assertThat(cache).as(\"使用 @CacheNamespace 指定了使用缓存\").isNotNull();\n        final long total = 5;\n        final long size = 3;\n        doTestAutoCommit(m -> {\n            Page<PageCache> page = new Page<>(1, size);\n            IPage<PageCache> result = m.selectPage(page, null);\n            assertThat(page).as(\"对象是同一个\").isEqualTo(result);\n            assertThat(result.getTotal()).isEqualTo(total);\n            assertThat(result.getRecords().size()).isEqualTo(size);\n        });\n        assertThat(cache.getSize()).as(\"一条count缓存一条分页缓存\").isEqualTo(2);\n\n\n        doTestAutoCommit(m -> {\n            Page<PageCache> page = new Page<>(1, size);\n            IPage<PageCache> result = m.selectPage(page, null);\n            assertThat(page).isEqualTo(result);\n            assertThat(result.getTotal()).isEqualTo(total);\n            assertThat(result.getRecords().size()).isEqualTo(size);\n        });\n        assertThat(cache.getSize()).as(\"因为命中缓存了所以还是2条\").isEqualTo(2);\n\n        doTestAutoCommit(m -> {\n            Page<PageCache> page = new Page<>(1, size);\n            page.addOrder(OrderItem.asc(\"id\"));\n            IPage<PageCache> result = m.selectPage(page, null);\n            assertThat(page).isEqualTo(result);\n            assertThat(result.getTotal()).isEqualTo(total);\n            assertThat(result.getRecords().size()).isEqualTo(size);\n        });\n        assertThat(cache.getSize()).as(\"条件不一样了,缓存变为3条\").isEqualTo(3);\n\n\n        doTestAutoCommit(m -> m.insert(new PageCache()));\n        assertThat(cache.getSize()).as(\"update 操作清除了所有缓存\").isEqualTo(0);\n\n\n        doTestAutoCommit(m -> {\n            Page<PageCache> page = new Page<>(1, size);\n            IPage<PageCache> result = m.selectPage(page, null);\n            assertThat(page).isEqualTo(result);\n            assertThat(result.getTotal()).isEqualTo(total + 1);\n            assertThat(result.getRecords().size()).isEqualTo(size);\n        });\n        assertThat(cache.getSize()).as(\"一条count缓存一条分页缓存\").isEqualTo(2);\n\n        doTest(i -> {\n            Page<?> page = new Page<>(1, size);\n            page.setCountId(\"otherCount\");\n            i.otherPage(page, new Tj());\n        });\n    }\n\n    @Test\n    void handlerTest() {\n        doTest(i -> {\n            Page<PageCache> page = new Page<>(1, 2);\n            i.otherPageHandler(page, new Tj(), new DefaultResultHandler());\n            System.out.println(page);\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into page_cache(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists page_cache\", \"CREATE TABLE IF NOT EXISTS page_cache (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Data\n    public static class Tj {\n        private boolean name = true;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCache.java",
    "content": "package com.baomidou.mybatisplus.test.cache.xml;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2022-03-07\n */\n@Data\npublic class XmlCache implements Serializable {\n    private static final long serialVersionUID = 907016853109330217L;\n    private Long id;\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCacheMapper.java",
    "content": "package com.baomidou.mybatisplus.test.cache.xml;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.CacheNamespaceRef;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@CacheNamespaceRef(XmlCacheMapper.class)\npublic interface XmlCacheMapper extends BaseMapper<XmlCache> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/cache/xml/XmlCacheTest.java",
    "content": "package com.baomidou.mybatisplus.test.cache.xml;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.cache.Cache;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\nclass XmlCacheTest extends BaseDbTest<XmlCacheMapper> {\n\n    @Test\n    void page() {\n        Cache cache = sqlSessionFactory.getConfiguration().getCache(XmlCacheMapper.class.getName());\n        assertThat(cache).as(\"使用 xml-cache 指定了使用缓存\").isNotNull();\n        final long total = 5;\n        doTestAutoCommit(m -> {\n            List<XmlCache> result = m.selectList(null);\n            assertThat(result).isNotNull();\n            assertThat(result.size()).isEqualTo(total);\n        });\n        assertThat(cache.getSize()).as(\"一条缓存\").isEqualTo(1);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into xml_cache(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists xml_cache\", \"CREATE TABLE IF NOT EXISTS xml_cache (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/ChainWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.test.chainwrapper;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class ChainWrapperTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        final String id = \"id\";\n        Entity entity = new Entity();\n        doTest(i -> i.queryChain()\n            .func(j -> j.isNotNull(id))\n            .func(entity.getId() != null, j -> j.eq(\"id\", entity.getId()))// 不会npe,也不会加入sql\n            .and(j -> j.isNotNull(id))\n            .or(j -> j.isNotNull(id))\n            .nested(j -> j.isNotNull(id))\n            .not(j -> j.isNull(id))\n            .list());\n        doTest(i -> i.queryChain().groupBy(\"id\").list());\n        doTest(i -> i.queryChain().groupBy(List.of(\"id\")).list());\n        doTest(i -> i.queryChain().groupBy(List.of(\"id\", \"name\")).list());\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/ChainWrappersTest.java",
    "content": "package com.baomidou.mybatisplus.test.chainwrapper;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\n\n/**\n * @author VampireAchao\n * @since 2022-04-18\n */\npublic class ChainWrappersTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        final String id = \"id\";\n        Entity entity = new Entity();\n        Assertions.assertAll(() -> ChainWrappers.queryChain(entity.getClass())\n            .func(j -> j.isNotNull(id))\n            .func(entity.getId() != null, j -> j.eq(\"id\", entity.getId()))// 不会npe,也不会加入sql\n            .and(j -> j.isNotNull(id))\n            .or(j -> j.isNotNull(id))\n            .nested(j -> j.isNotNull(id))\n            .not(j -> j.isNull(id))\n            .list());\n\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.chainwrapper;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/chainwrapper/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.chainwrapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    default QueryChainWrapper<Entity> queryChain() {\n        return ChainWrappers.queryChain(this);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\n@Accessors(chain = true)\n@TableName(autoResultMap = true)\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 5094767605376915138L;\n\n    private Long id;\n\n    private EnumStr enumStr;\n\n    private EnumInt enumInt;\n    /**\n     * 使用 EnumOrdinalTypeHandler\n     */\n    private EnumOrdinal enumOrdinal;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    Entity findById(Long id);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumInt.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Getter\n@AllArgsConstructor\npublic enum EnumInt {\n    ONE(1),\n    TWO(2);\n\n    @EnumValue\n    private final int value;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumOrdinal.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport lombok.Getter;\n\n/**\n * @author miemie\n * @since 2020-07-21\n */\n@Getter\npublic enum EnumOrdinal {\n    ONE,\n    TWO,\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumStr.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Getter\n@AllArgsConstructor\npublic enum EnumStr {\n    ONE(\"1\"),\n    TWO(\"2\");\n\n    @EnumValue\n    private final String value;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/enums/EnumTest.java",
    "content": "package com.baomidou.mybatisplus.test.enums;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\nclass EnumTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        Long id = 1L;\n        doTestAutoCommit(m -> {\n            Entity entity = new Entity().setId(id).setEnumInt(EnumInt.ONE).setEnumStr(EnumStr.TWO).setEnumOrdinal(EnumOrdinal.TWO);\n            int insert = m.insert(entity);\n            assertThat(insert).as(\"插入成功\").isEqualTo(1);\n        });\n\n        doTestAutoCommit(m -> {\n            Entity entity = m.selectById(id);\n            assertThat(entity).as(\"查出刚刚插入的数据\").isNotNull();\n            assertThat(entity.getEnumInt()).as(\"枚举正确\").isEqualTo(EnumInt.ONE);\n            assertThat(entity.getEnumStr()).as(\"枚举正确\").isEqualTo(EnumStr.TWO);\n            assertThat(entity.getEnumOrdinal()).as(\"枚举正确\").isEqualTo(EnumOrdinal.TWO);\n            entity.setEnumOrdinal(EnumOrdinal.ONE);\n            m.updateById(entity);\n        });\n\n        doTest(m -> {\n            Entity entity = m.findById(id);\n            assertThat(entity).as(\"查出刚刚插入的数据\").isNotNull();\n            assertThat(entity.getEnumInt()).as(\"枚举正确\").isEqualTo(EnumInt.ONE);\n            assertThat(entity.getEnumStr()).as(\"枚举正确\").isEqualTo(EnumStr.TWO);\n            assertThat(entity.getEnumOrdinal()).as(\"枚举正确\").isEqualTo(EnumOrdinal.ONE);\n        });\n    }\n\n    @Override\n    protected Consumer<Configuration> consumer() {\n        return i -> i.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"enum_int integer NULL DEFAULT NULL,\\n\" +\n                \"enum_str VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"enum_ordinal integer NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillEntity.java",
    "content": "package com.baomidou.mybatisplus.test.fill;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * @author nieqiurong 2023年9月29日\n */\n@Data\n@TableName(value = \"t_fill\")\npublic class FillEntity {\n\n    private Long id;\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String name;\n\n    private transient int seq;\n\n    public FillEntity() {\n    }\n\n    public FillEntity(Long id) {\n        this.id = id;\n    }\n\n    public FillEntity(Long id, String name) {\n        this.id = id;\n        this.name = name;\n    }\n\n    void addSeq(){\n        this.seq ++;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillMapper.java",
    "content": "package com.baomidou.mybatisplus.test.fill;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong 2023年9月29日\n */\npublic interface FillMapper extends BaseMapper<FillEntity> {\n\n    void insertBatch1(List<FillEntity> entityList);\n\n    void insertBatch2(@Param(\"mpList\") List<FillEntity> entityList);\n\n    void insertBatch3(@Param(\"mybatisList\") List<FillEntity> entityList);\n\n    void insertBatch4(@Param(\"list\") List<FillEntity> entityList, String a, String b, String c);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/fill/FillTest.java",
    "content": "package com.baomidou.mybatisplus.test.fill;\n\nimport com.baomidou.mybatisplus.core.batch.MybatisBatch;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author nieqiurong 2023年9月29日\n */\npublic class FillTest extends BaseDbTest<FillMapper> {\n\n    @Test\n    void testInsert() {\n        doTest(mapper -> {\n            var entity = new FillEntity();\n            mapper.insert(entity);\n            assertEntity(entity, \"insertAdmin\", 1);\n        });\n    }\n\n    @Test\n    void testInsertBatch1() {\n        doTest(mapper -> {\n            var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity()));\n            mapper.insertBatch1(entityList);\n            for (FillEntity entity : entityList) {\n                assertEntity(entity, \"insertAdmin\", 1);\n            }\n        });\n    }\n\n    @Test\n    void testInsertBatch2() {\n        doTest(mapper -> {\n            var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity()));\n            mapper.insertBatch2(entityList);\n            for (FillEntity entity : entityList) {\n                assertEntity(entity, \"insertAdmin\", 1);\n            }\n        });\n    }\n\n    @Test\n    void testInsertBatch3() {\n        doTest(mapper -> {\n            var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity()));\n            mapper.insertBatch3(entityList);\n            for (FillEntity entity : entityList) {\n                assertEntity(entity, \"insertAdmin\", 1);\n            }\n        });\n    }\n\n    @Test\n    void testInsertBatch4() {\n        doTest(mapper -> {\n            var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity()));\n            mapper.insertBatch4(entityList, \"\", \"\", \"\");\n            for (FillEntity entity : entityList) {\n                assertEntity(entity, \"insertAdmin\", 1);\n            }\n        });\n    }\n\n    @Test\n    void testBatch() {\n        var entityList = new ArrayList<>(Arrays.asList(new FillEntity(), new FillEntity()));\n        var method = new MybatisBatch.Method<FillEntity>(FillMapper.class);\n        MybatisBatchUtils.execute(sqlSessionFactory, entityList, method.insert());\n        for (FillEntity entity : entityList) {\n            assertEntity(entity, \"insertAdmin\", 1);\n        }\n    }\n\n    void assertEntity(FillEntity entity, String name, int seq) {\n        Assertions.assertEquals(entity.getName(), name);\n        Assertions.assertEquals(entity.getSeq(), seq);\n    }\n\n    @Override\n    protected GlobalConfig globalConfig() {\n        GlobalConfig globalConfig = super.globalConfig();\n        globalConfig.setMetaObjectHandler(new MetaObjectHandler() {\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                var entity = (FillEntity) metaObject.getOriginalObject();\n                entity.setName(\"insertAdmin\");\n                entity.addSeq();\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                var entity = (FillEntity) metaObject.getOriginalObject();\n                entity.setName(\"updateAdmin\");\n                entity.addSeq();\n\n            }\n        });\n        return globalConfig;\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_fill\", \"CREATE TABLE IF NOT EXISTS t_fill (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/Demo.java",
    "content": "package com.baomidou.mybatisplus.test.gaussdb;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\nimport lombok.ToString;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\n\n/**\n * @author nieqiurong\n * @since 3.5.13\n */\n@Data\n@ToString\npublic class Demo {\n\n    @TableId(type = IdType.AUTO)\n    private Long id;\n\n    private String name;\n\n    private Integer age;\n\n    private LocalDate birthday;\n\n    private LocalDateTime createTime;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/DemoMapper.java",
    "content": "package com.baomidou.mybatisplus.test.gaussdb;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author nieqiurong\n * @since 3.5.13\n */\n@Mapper\npublic interface DemoMapper extends BaseMapper<Demo> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/gaussdb/GaussdbTest.java",
    "content": "package com.baomidou.mybatisplus.test.gaussdb;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport com.huawei.gaussdb.jdbc.Driver;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.apache.ibatis.datasource.pooled.PooledDataSource;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.sql.DataSource;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\n/**\n * @author nieqiurong\n * @since 3.5.13\n */\npublic class GaussdbTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(GaussdbTest.class);\n\n    private static final String CREATE_TABLE = \"\"\"\n\n        DROP TABLE IF EXISTS \"demo\";\n\n        CREATE TABLE \"demo\" (\n          \"id\" bigserial,\n          \"name\" varchar(255),\n          \"age\" int4,\n          \"birthday\" date,\n          \"create_time\" timestamp,\n          PRIMARY KEY (\"id\")\n        )\n        ;\n\n        COMMENT ON COLUMN \"demo\".\"id\" IS '主键';\n\n        COMMENT ON COLUMN \"demo\".\"name\" IS '姓名';\n\n        COMMENT ON COLUMN \"demo\".\"age\" IS '年龄';\n\n        COMMENT ON COLUMN \"demo\".\"birthday\" IS '生日';\n\n        COMMENT ON COLUMN \"demo\".\"create_time\" IS '创建时间';\n        \"\"\";\n\n    private static SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.addMapper(DemoMapper.class);\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n        sqlSessionFactory.setConfiguration(configuration);\n        return sqlSessionFactory.getObject();\n    }\n\n    private static Stream<DbConfig> dataSource() {\n        String host = \"127.0.0.1\";\n        int port = 8000;\n        String user = \"root\";\n        String dbPassword = \"123456\";\n        return Stream.of(\n            new DbConfig(\"mysql\", new PooledDataSource(Driver.class.getName(), \"jdbc:gaussdb://\" + host + \":\" + port + \"/test_mysql\", user, dbPassword)),\n            new DbConfig(\"oracle\", new PooledDataSource(Driver.class.getName(), \"jdbc:gaussdb://\" + host + \":\" + port + \"/test_oracle\", user, dbPassword))\n        );\n    }\n\n    @Data\n    @AllArgsConstructor\n    private static class DbConfig {\n\n        private final String name;\n\n        private final DataSource dataSource;\n\n        @Override\n        public String toString() {\n            return name;\n        }\n\n    }\n\n\n    @ParameterizedTest\n    @MethodSource(\"dataSource\")\n    void test(DbConfig dbConfig) throws Exception {\n        DataSource dataSource = dbConfig.getDataSource();\n        new SqlRunner(dataSource.getConnection()).run(CREATE_TABLE);\n        try (SqlSession sqlSession = sqlSessionFactory(dataSource).openSession(true)) {\n            DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);\n            LOGGER.info(\"delete all data:{}\", demoMapper.delete(Wrappers.emptyWrapper()));\n\n            Demo demo = new Demo();\n            demo.setAge(12);\n            demo.setName(\"12岁的用户\");\n            demo.setBirthday(LocalDate.of(2013, 1, 1));\n            demo.setCreateTime(LocalDateTime.now());\n            LOGGER.info(\"insert data:{}\", demoMapper.insert(demo));\n\n            Demo selectDemo = demoMapper.selectById(demo.getId());\n            Assertions.assertNotNull(selectDemo);\n            LOGGER.info(\"select data:{}\", selectDemo);\n\n            Demo updateDemo = new Demo();\n            updateDemo.setAge(23);\n            updateDemo.setId(selectDemo.getId());\n            Assertions.assertEquals(1, demoMapper.updateById(updateDemo));\n\n            LOGGER.info(\"delete by id:{}\", demoMapper.deleteById(demo));\n\n            List<Demo> demoList = new ArrayList<>();\n            for (int i = 0; i < 50; i++) {\n                demo = new Demo();\n                demo.setAge(1);\n                demo.setName(\"demo\" + (i + 1));\n                demo.setBirthday(LocalDate.of(2020, 12, 1));\n                demo.setCreateTime(LocalDateTime.now());\n                demoList.add(demo);\n            }\n            LOGGER.info(\"insert data on batch:{}\", demoMapper.insert(demoList));\n\n            Long count = demoMapper.selectCount(Wrappers.emptyWrapper());\n            LOGGER.info(\"select count:{}\", count);\n            Assertions.assertEquals(50L, count);\n\n            Page<Demo> page = demoMapper.selectPage(new Page<>(1, 10), Wrappers.emptyWrapper());\n            Assertions.assertEquals(50, page.getTotal());\n            Assertions.assertEquals(10, page.getSize());\n            for (Demo record : page.getRecords()) {\n                LOGGER.info(\"record:{}\", record);\n            }\n            LOGGER.info(\"next page .\");\n            page = demoMapper.selectPage(new Page<>(2, 10), Wrappers.emptyWrapper());\n            Assertions.assertEquals(50, page.getTotal());\n            Assertions.assertEquals(10, page.getSize());\n            for (Demo record : page.getRecords()) {\n                LOGGER.info(\"record:{}\", record);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/ActiveRecordTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.service.IH2StudentService;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n\n/**\n * ActiveRecord 测试\n *\n * @author nieqiurong 2018/7/27.\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass ActiveRecordTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(ActiveRecordTest.class);\n\n    @Autowired\n    private IH2StudentService h2StudentService;\n\n    @Test\n    @Transactional\n    @Order(1)\n    void testInsert() {\n        H2Student student = new H2Student(3L, \"测试学生\", 2);\n        assertThat(student.insert()).isTrue();\n        System.out.println(student.getId());\n        assertThat(student.getId()).isEqualTo(3);\n        student.setId(null);\n        assertThat(student.insert()).isTrue();\n        System.out.println(student.getId());\n    }\n\n    @Test\n    @Order(2)\n    void testUpdate() {\n        H2Student student = new H2Student(1L, \"Tom长大了\", 2);\n        Assertions.assertTrue(student.updateById());\n        student.setName(\"不听话的学生\");\n        Assertions.assertTrue(student.update(new QueryWrapper<H2Student>().gt(\"id\", 10)));\n    }\n\n    @Test\n    @Order(3)\n    void testSelect() {\n        H2Student student = new H2Student();\n        student.setId(1L);\n        Assertions.assertNotNull(student.selectById());\n        Assertions.assertNotNull(student.selectById(1L));\n    }\n\n    @Test\n    @Order(4)\n    void testSelectList() {\n        H2Student student = new H2Student();\n        List<H2Student> students = student.selectList(new QueryWrapper<>(student));\n        students.forEach($this -> LOGGER.info(\"用户信息:{}\", $this));\n        Assertions.assertTrue(students.size() > 1);\n    }\n\n    @Test\n    @Order(5)\n    void testSelectPage() {\n        IPage<H2Student> page = new Page<>(1, 10);\n        H2Student student = new H2Student();\n        page = student.selectPage(page, new QueryWrapper<>(student));\n        List<H2Student> records = page.getRecords();\n        LOGGER.info(\"总数:{}\", page.getTotal());\n        records.forEach($this -> LOGGER.info(\"用户信息:{}\", $this));\n        Assertions.assertTrue(page.getTotal() > 1);\n    }\n\n    @Test\n    @Order(6)\n    void testSelectCount() {\n        H2Student student = new H2Student();\n        long count = new H2Student().selectCount(new QueryWrapper<>(student));\n        LOGGER.info(\"count:{}\", count);\n        Assertions.assertTrue(count > 1);\n    }\n\n    @Test\n    @Order(7)\n    void testInsertOrUpdate() {\n        H2Student student = new H2Student(2L, \"Jerry也长大了\", 2);\n        Assertions.assertTrue(student.insertOrUpdate());\n        student.setId(null);\n        Assertions.assertTrue(student.insertOrUpdate());\n    }\n\n    @Test\n    @Order(8)\n    void testSelectAll() {\n        H2Student student = new H2Student();\n        List<H2Student> students = student.selectAll();\n        Assertions.assertNotNull(students);\n        students.forEach($this -> LOGGER.info(\"用户信息:{}\", $this));\n    }\n\n    @Test\n    @Order(9)\n    void testSelectOne() {\n        H2Student student = new H2Student();\n        Assertions.assertNotNull(student.selectOne(new QueryWrapper<>()));\n    }\n\n    @Test\n    @Order(10)\n    void testTransactional() {\n        try {\n            h2StudentService.testTransactional();\n        } catch (MybatisPlusException e) {\n            List<H2Student> students = new H2Student().selectList(new QueryWrapper<H2Student>().lambda().like(H2Student::getName, \"tx\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(students));\n        }\n    }\n\n    @Test\n    @Order(11)\n    void testDelete() {\n        H2Student student = new H2Student();\n        student.setId(2L);\n        Assertions.assertTrue(student.deleteById());\n        Assertions.assertTrue(student.deleteById(12L));\n        Assertions.assertTrue(student.delete(new QueryWrapper<H2Student>().gt(\"id\", 10)));\n    }\n\n    @Test\n    @Order(12)\n    void sqlCommentTest() {\n        String name = \"name1\", nameNew = \"name1New\";\n        H2Student student = new H2Student().setName(name).setAge(2);\n        student.delete(new QueryWrapper<H2Student>().comment(\"deleteAllStu\"));\n        Assertions.assertTrue(student.insert());\n        boolean updated = new H2Student().setName(nameNew).update(new QueryWrapper<H2Student>().comment(\"updateStuName1\").lambda()\n            .eq(H2Student::getName, name)\n        );\n        Assertions.assertTrue(updated);\n        H2Student h2Student = student.selectOne(\n            new QueryWrapper<H2Student>().lambda().comment(\"getStuByUniqueName\")\n                .eq(H2Student::getName, nameNew)\n        );\n        Assertions.assertNotNull(h2Student);\n        LambdaQueryWrapper<H2Student> queryWrapper = new QueryWrapper<H2Student>().lambda().ge(H2Student::getAge, 1);\n        long userCount = student.selectCount(queryWrapper.comment(\"getStuCount\"));\n        Assertions.assertEquals(1, userCount);\n        List<H2Student> h2StudentList = student.selectList(queryWrapper.comment(\"getStuList\"));\n        Assertions.assertEquals(1, h2StudentList.size());\n        IPage<H2Student> h2StudentIPage = student.selectPage(new Page<>(1, 10), queryWrapper.comment(\"getStuPage\"));\n        Assertions.assertEquals(1, h2StudentIPage.getRecords().size());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/BaseTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.test.annotation.DirtiesContext;\n\nimport java.util.List;\n\n@DirtiesContext\npublic class BaseTest {\n\n    @Autowired\n    protected JdbcTemplate jdbcTemplate;\n\n    protected static final String NQQ = \"聂秋秋\";\n\n    protected void log(Object object) {\n        System.out.println(object);\n    }\n\n    protected List<H2User> queryByName(String name) {\n        String sql = \"select TEST_ID, NAME, AGE,LAST_UPDATED_DT from h2user \";\n        if (name != null) {\n            sql += \"where name='\" + name + \"'\";\n        }\n        return jdbcTemplate.query(sql, (rs, rowNum) -> {\n            H2User u = new H2User();\n            u.setTestId(rs.getLong(\"TEST_ID\"));\n            u.setName(rs.getString(\"NAME\"));\n            u.setAge(AgeEnum.parseValue(rs.getInt(\"AGE\")));\n            u.setLastUpdatedDt(rs.getDate(\"LAST_UPDATED_DT\"));\n            return u;\n        });\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/CustomFillTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.test.h2.customfill.mapper.TestModelMapper;\nimport com.baomidou.mybatisplus.test.h2.customfill.model.TestModel;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-custom-fill-test-h2.xml\"})\npublic class CustomFillTest {\n\n    @Autowired\n    private TestModelMapper testModelMapper;\n\n    @BeforeAll\n    public static void before(){\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), TestModel.class);\n    }\n\n    @Test\n    public void testInsert() {\n\n        TestModel testModel = new TestModel();\n        testModelMapper.insert(testModel);\n        Assertions.assertNotNull(testModel.getA());\n        Assertions.assertNotNull(testModel.getB());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/FillPerformanceTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.service.IPerformanceModelService;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-fill-performance-h2.xml\"})\nclass FillPerformanceTest {\n\n    @Autowired\n    private IPerformanceModelService performanceModelService;\n\n    @Test\n    void test(){\n        List<PerformanceModel> list = new ArrayList<>();\n        for (int i = 0; i < 5000; i++) {\n            list.add(new PerformanceModel());\n        }\n        System.out.println(\"-------------------------\");\n        long start = System.currentTimeMillis();\n        performanceModelService.saveBatch(list);\n        long end = System.currentTimeMillis();\n        System.out.println(end - start);\n        System.out.println(\"-------------------------\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2Delete1Eq1Test.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author hubin\n * @since 2018-06-05\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass H2Delete1Eq1Test extends BaseTest {\n\n    @Autowired\n    protected H2UserMapper logicDeleteMapper;\n    @Autowired\n    protected H2StudentMapper defaultMapper;\n\n    @Test\n    @Order(1)\n    void crudTest() {\n        for (int i = 0; i < 10; i++) {\n            logicDeleteMapper.insert(new H2User(\"mp\" + i, AgeEnum.ONE));\n        }\n        log(logicDeleteMapper.selectList(new QueryWrapper<H2User>().orderByAsc(\"`desc`\")));\n        log(logicDeleteMapper.selectOne(new QueryWrapper<H2User>().last(\"limit 1\")));\n\n        H2User h2User = new H2User();\n        h2User.setDesc(\"1\");\n        h2User.setName(\"2\");\n        log(logicDeleteMapper.selectList(new QueryWrapper<>(h2User).orderByAsc(\"name\")));\n\n        for (long i = 30; i < 50L; i++) {\n            defaultMapper.insert(new H2Student(i, \"Tom长大了\", 1));\n        }\n        log(logicDeleteMapper.selectList(new QueryWrapper<>(h2User).eq(\"name\", \"2\").orderByAsc(\"name\")));\n        log(defaultMapper.selectList(new QueryWrapper<H2Student>().orderByAsc(\"id\")));\n        log(defaultMapper.selectOne(new QueryWrapper<H2Student>().last(\"limit 1\")));\n\n        H2Student h2Student = new H2Student();\n        h2Student.setId(1L);\n        h2Student.setAge(2);\n        log(defaultMapper.selectList(new QueryWrapper<>(h2Student).orderByAsc(\"id\")));\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    void delete() {\n        logicDeleteMapper.delete(new QueryWrapper<>());\n        defaultMapper.delete(new QueryWrapper<>());\n        Assertions.assertEquals(0, logicDeleteMapper.selectCount(new QueryWrapper<>()));\n        List<H2User> userList = queryByName(null);\n        System.out.println(userList.size());\n        Assertions.assertNotEquals(0, userList.size());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2KeyGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.test.h2.keygenerator.mapper.*;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.*;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-keygenerator-h2.xml\"})\nclass H2KeyGeneratorTest {\n\n    @Autowired\n    private KeyGeneratorMapper keyGeneratorMapper;\n\n    @Autowired\n    private LongKeyGeneratorMapper longKeyGeneratorMapper;\n\n    @Autowired\n    private StringKeyGeneratorMapper stringKeyGeneratorMapper;\n\n    @Autowired\n    private ExtendKeyGeneratorMapper extendKeyGeneratorMapper;\n\n    @Autowired\n    private IntegerKeyGeneratorMapper integerKeyGeneratorMapper;\n\n    @Test\n    void test() {\n        KeyGeneratorModel keyGeneratorModel = new KeyGeneratorModel();\n        keyGeneratorModel.setName(\"我举起了咩咩\");\n        keyGeneratorMapper.insert(keyGeneratorModel);\n        Assertions.assertNotNull(keyGeneratorModel.getUid());\n        Assertions.assertEquals(1L, keyGeneratorModel.getUid());\n\n        LongKeyGeneratorModel longKeyGeneratorModel = new LongKeyGeneratorModel();\n        longKeyGeneratorModel.setName(\"我举起了个栗子\");\n        longKeyGeneratorMapper.insert(longKeyGeneratorModel);\n        Assertions.assertNotNull(longKeyGeneratorModel.getId());\n        Assertions.assertEquals(2L, longKeyGeneratorModel.getId());\n\n        StringKeyGeneratorModel stringKeyGeneratorModel = new StringKeyGeneratorModel();\n        stringKeyGeneratorModel.setName(\"我举起了个锤子\");\n        stringKeyGeneratorMapper.insert(stringKeyGeneratorModel);\n        Assertions.assertNotNull(stringKeyGeneratorModel.getId());\n        Assertions.assertEquals(\"3\", stringKeyGeneratorModel.getId());\n\n        ExtendKeyGeneratorModel extendKeyGeneratorModel = new ExtendKeyGeneratorModel();\n        extendKeyGeneratorModel.setName(\"我举起了句号\");\n        extendKeyGeneratorMapper.insert(extendKeyGeneratorModel);\n        Assertions.assertNotNull(extendKeyGeneratorModel.getUid());\n        Assertions.assertEquals(4L, extendKeyGeneratorModel.getUid());\n\n        //这个受限数据库，如果返回是long的话,那就救不了.\n        IntegerKeyGeneratorModel integerKeyGeneratorModel = new IntegerKeyGeneratorModel();\n        integerKeyGeneratorModel.setName(\"我举起了K神\");\n        integerKeyGeneratorMapper.insert(integerKeyGeneratorModel);\n        Assertions.assertNotNull(integerKeyGeneratorModel.getUid());\n        Assertions.assertEquals(5, integerKeyGeneratorModel.getUid());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2LogicDeleteTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.entity.H2UserLogicDelete;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserLogicDeleteMapper;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author hubin\n * @since 2018-06-05\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-logic-delete-h2.xml\"})\nclass H2LogicDeleteTest extends BaseTest {\n\n    @Autowired\n    protected H2UserLogicDeleteMapper logicDeleteMapper;\n\n    @Test\n    @Order(1)\n    void crudTest() {\n        String name = \"LogicDelete4Date\";\n        H2UserLogicDelete user = new H2UserLogicDelete();\n        user.setAge(AgeEnum.ONE);\n        user.setName(name);\n        logicDeleteMapper.insert(user);\n        Assertions.assertNotNull(user.getTestId(), \"id should not be null\");\n        Assertions.assertNull(user.getLastUpdatedDt());\n\n        logicDeleteMapper.deleteById(user.getTestId());\n\n        List<H2User> userList = queryByName(name);\n        Assertions.assertTrue(userList!=null && !userList.isEmpty());\n        Assertions.assertNotNull(userList.get(0).getLastUpdatedDt(),\"lastUpdateDt should not be null after logic delete\");\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2MetaObjectHandler.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport org.apache.ibatis.reflection.MetaObject;\n\nimport java.sql.Timestamp;\nimport java.util.Date;\n\n\n/**\n * 测试，自定义元对象字段填充控制器，实现公共字段自动写入\n *\n * @author hubin\n * @since 2017-06-25\n */\npublic class H2MetaObjectHandler implements MetaObjectHandler {\n\n    /**\n     * 测试 user 表 name 字段为空自动填充\n     */\n    @Override\n    public void insertFill(MetaObject metaObject) {\n        // System.out.println(\"*************************\");\n        // System.out.println(\"insert fill\");\n        // System.out.println(\"*************************\");\n\n        // 测试下划线\n        Object testType = this.getFieldValByName(\"testType\", metaObject);\n        // System.out.println(\"testType=\" + testType);\n        if (testType == null) {\n            //测试实体没有的字段，配置在公共填充，不应该set到实体里面\n            this.strictInsertFill(metaObject, \"testType1\", Integer.class, 3);\n            this.strictInsertFill(metaObject, \"testType\", Integer.class, 3);\n        }\n    }\n\n    @Override\n    public void updateFill(MetaObject metaObject) {\n        // System.out.println(\"*************************\");\n        // System.out.println(\"update fill\");\n        // System.out.println(\"*************************\");\n        //测试实体没有的字段，配置在公共填充，不应该set到实体里面\n        this.strictUpdateFill(metaObject, \"lastUpdatedDt1\", Date.class, new Timestamp(System.currentTimeMillis()));\n        this.strictUpdateFill(metaObject, \"lastUpdatedDt\", Date.class, new Timestamp(System.currentTimeMillis()));\n    }\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2StudentMapperTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.enums.GenderEnum;\nimport com.baomidou.mybatisplus.test.h2.enums.GradeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author hubin\n * @since 2018-06-05\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass H2StudentMapperTest extends BaseTest {\n\n    @Autowired\n    protected H2StudentMapper studentMapper;\n\n    @Test\n    @Order(1)\n    void crudTest() {\n//        H2Student stu = new H2Student();\n//        stu.setGrade(GradeEnum.HIGH);\n//        studentMapper.update(stu, null);\n        H2Student student = new H2Student();\n        Long id = 10086L;\n        student.setId(id);\n        student.setAge(188);\n        student.setGender(GenderEnum.MALE);\n        student.setGrade(GradeEnum.PRIMARY);\n        studentMapper.insert(student);\n\n        List<H2Student> list = studentMapper.selectList(new QueryWrapper<>());\n        for (H2Student s : list) {\n            System.out.println(s.getGrade());\n            if (Objects.equals(s.getId(), id)) {\n                Assert.notNull(s.getGrade(), \"id=\" + id + \" should have grade\");\n                Assert.notNull(s.getGender(), \"id=\" + id + \" should have gender\");\n            }\n        }\n        H2Student updateStu = new H2Student();\n        updateStu.setId(15L);\n        updateStu.setGrade(GradeEnum.HIGH);\n        updateStu.setGender(GenderEnum.FEMALE);\n        Assert.isTrue(studentMapper.updateById(updateStu) == 1, \"should update success\");\n        updateStu = studentMapper.selectById(15L);\n        Assert.notNull(updateStu.getGrade(), \"grade should updated\");\n        Assert.notNull(updateStu.getGender(), \"gender should updated\");\n\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    void pageCountZeroTest() {\n        IPage<H2Student> page = studentMapper.selectPage(new Page<>(), Wrappers.<H2Student>query().eq(\"name\", \"无\"));\n        if (null != page) {\n            System.out.println(\"total: \" + page.getTotal());\n        }\n    }\n\n    /**\n     * group 或者 order 测试\n     */\n    @Test\n    void groupByOrderBy() {\n        LambdaQueryWrapper<H2Student> wrapper = Wrappers.<H2Student>lambdaQuery().groupBy(H2Student::getAge);\n        LambdaQueryWrapper<H2Student> wrapper2 = Wrappers.<H2Student>lambdaQuery().orderByAsc(H2Student::getAge);\n        System.out.println(wrapper.getSqlSegment());\n        Assertions.assertEquals(\" GROUP BY age\", wrapper.getSqlSegment());\n        Assertions.assertEquals(\" ORDER BY age ASC\", wrapper2.getSqlSegment());\n    }\n\n    @Test\n    void testDeleteByIdWithEntity() {\n        H2Student h2Student = new H2Student(111L, \"测试根据实体删除\", 12);\n        studentMapper.insert(h2Student);\n        Assertions.assertEquals(1, studentMapper.deleteById(h2Student));\n    }\n\n    @Test\n    void testIn() {\n        LambdaQueryWrapper<H2Student> wrapper = Wrappers.<H2Student>lambdaQuery().in(H2Student::getName, Arrays.asList(\"a\", \"b\"));\n        studentMapper.selectList(wrapper);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserMapperTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.batch.MybatisBatch;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.IdWorker;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.entity.SuperEntity;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport org.apache.ibatis.exceptions.PersistenceException;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.IntStream;\n\nimport static java.util.stream.Collectors.toList;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author hubin\n * @since 2018-06-05\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass H2UserMapperTest extends BaseTest {\n\n    @Autowired\n    protected H2UserMapper userMapper;\n\n    @Autowired\n    private SqlSessionFactory sqlSessionFactory;\n\n    @Autowired\n    private TransactionTemplate transactionTemplate;\n\n    @Test\n    void testMapperSaveBatch() {\n        var list = List.of(new H2User(\"秋秋1\"), new H2User(\"秋秋2\"));\n        List<BatchResult> batchResults = userMapper.insert(list);\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        Assertions.assertEquals(2, batchResults.getFirst().getUpdateCounts().length);\n    }\n\n    @Test\n    void testMapperUpdateBatch() {\n        var list = List.of(new H2User(\"秋秋1\"), new H2User(\"秋秋2\"));\n        userMapper.insert(list);\n        for (H2User h2User : list) {\n            h2User.setName(\"test\" + 1);\n        }\n        List<BatchResult> batchResults = userMapper.updateById(list);\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        Assertions.assertEquals(2, batchResults.getFirst().getUpdateCounts().length);\n    }\n\n\n    @Test\n    void testBatchTransaction() {\n        List<H2User> h2UserList = Arrays.asList(new H2User(1000036L, \"测试12323232\"), new H2User(10000367L, \"测试3323232\"));\n        try {\n            transactionTemplate.execute((TransactionCallback<List<BatchResult>>) status -> {\n                MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n                // 执行批量插入\n                MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert());\n                throw new RuntimeException(\"出错了\");\n            });\n        } catch (Exception exception) {\n            for (H2User h2User : h2UserList) {\n                Assertions.assertNull(userMapper.selectById(h2User.getTestId()));\n            }\n        }\n        transactionTemplate.execute(status -> {\n            MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n            // 执行批量插入\n            return MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert());\n        });\n        for (H2User h2User : h2UserList) {\n            Assertions.assertNotNull(userMapper.selectById(h2User.getTestId()));\n        }\n    }\n\n    @Test\n    void testInsertBatch() {\n        int batchSize = 1000;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(\"test\" + i));\n        }\n        MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n        // 执行批量插入\n        List<BatchResult> batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert());\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        int[] updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n        List<Long> ids = Arrays.asList(120000L, 120001L);\n        MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);\n        Assertions.assertTrue(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.insert(H2User::ofId))));\n    }\n\n    @Test\n    void testInsertBatchByCustomMethod() {\n        int batchSize = 1000;\n        List<BatchResult> batchResults;\n        int[] updateCounts;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(\"myInsertWithoutParam\" + i));\n        }\n        MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);\n        // 执行批量插入\n        batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get(\"myInsertWithoutParam\"));\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n        h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(\"myInsertWithParam\" + i));\n        }\n        // 执行批量插入\n        batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get(\"myInsertWithParam\", parameter -> Map.of(\"user1\", parameter)));\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n        h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(\"myInsertWithParam\" + i));\n        }\n        batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.get(\"myInsertWithParam\", parameter -> Map.of(\"user1\", parameter)));\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n    }\n\n    @Test\n    void testDeleteByIds() {\n        List<BatchResult> batchResults;\n        int batchSize = 1000;\n        List<Long> ids = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            ids.add((long) 7120000 + i);\n        }\n        List<H2User> userList = new ArrayList<>();\n        MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);\n        // 转换成实体进行逻辑删除\n        batchResults = MybatisBatchUtils.execute(sqlSessionFactory, ids, method.deleteById(id -> {\n            H2User h2User = H2User.ofId(id);\n            userList.add(h2User);\n            return h2User;\n        }));\n        Assertions.assertFalse(SqlHelper.retBool(batchResults));\n        int[] updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n        for (H2User h2User : userList) {\n            Assertions.assertNotNull(h2User.getLastUpdatedDt());\n        }\n        // 不能走填充\n        batchResults = MybatisBatchUtils.execute(sqlSessionFactory, ids, method.deleteById());\n        Assertions.assertFalse(SqlHelper.retBool(batchResults));\n        updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n    }\n\n    @Test\n    void testUpdateBatch() {\n        int batchSize = 1000;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(Long.valueOf(30000 + i), \"test\" + i));\n        }\n        MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n        // 执行批量更新\n        List<BatchResult> batchResults = MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.updateById());\n        Assertions.assertFalse(SqlHelper.retBool(batchResults));\n        int[] updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n\n        List<Long> ids = Arrays.asList(120000L, 120001L);\n        MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);\n\n        Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.update(id -> Wrappers.<H2User>lambdaUpdate().set(H2User::getName, \"updateTest\").eq(H2User::getTestId, id)))));\n        Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, ids, method.update(id -> new H2User().setName(\"updateTest2\"), id -> Wrappers.<H2User>lambdaUpdate().eq(H2User::getTestId, id)))));\n\n        Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.update(user -> Wrappers.<H2User>update().set(\"name\", \"updateTest3\").eq(\"test_id\", user.getTestId())))));\n        Assertions.assertFalse(SqlHelper.retBool(MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, method.update(user -> new H2User(\"updateTests4\"), p -> Wrappers.<H2User>update().eq(\"test_id\", p.getTestId())))));\n    }\n\n    @Test\n    void testSaveOrUpdateBatch1() {\n        int batchSize = 10;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(Long.valueOf(40000 + i), \"test\" + i));\n        }\n        MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n        List<BatchResult> batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList,\n                mapperMethod.insert(),\n                ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null),\n                mapperMethod.updateById());\n        // 没有使用共享的sqlSession,由于都是新增返回还是一个批次\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        int[] updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n    }\n\n    @Test\n    void testSaveOrUpdateBatchMapper1() {\n        int batchSize = 10;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(Long.valueOf(140000 + i), \"test\" + i));\n        }\n        List<BatchResult> batchResults = userMapper.insertOrUpdate(h2UserList);\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        Assertions.assertEquals(batchSize, batchResults.size());\n        // 使用共享的sqlSession,等于每次都是刷新了,批次总结果集就等于数据大小了\n        for (BatchResult batchResult : batchResults) {\n            Assertions.assertEquals(1, batchResult.getUpdateCounts().length);\n            Assertions.assertEquals(1, batchResult.getUpdateCounts()[0]);\n        }\n    }\n\n    @Test\n    void testSaveOrUpdateBatchMapper2() {\n        int batchSize = 10;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(Long.valueOf(40000 + i), \"test\" + i));\n        }\n        List<BatchResult> batchResults = userMapper.insertOrUpdate(h2UserList,((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null));\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        // 没有使用共享的sqlSession,由于都是新增返回还是一个批次\n        int[] updateCounts = batchResults.getFirst().getUpdateCounts();\n        Assertions.assertEquals(batchSize, updateCounts.length);\n    }\n\n    @Test\n    void testSaveOrUpdateBatchMapper3() {\n        var id = IdWorker.getId();\n        var h2UserList = List.of(new H2User(id, \"testSaveOrUpdateBatchMapper3\"), new H2User(id, \"testSaveOrUpdateBatchMapper3-1\"));\n        // 由于没有共享一个sqlSession,第二条记录selectById的时候第一个sqlSession的数据还没提交,会执行插入导致主键冲突.\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> userMapper.insertOrUpdate(h2UserList, ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null)));\n    }\n\n    @Test\n    void testSaveOrUpdateBatchMapper4() {\n        var id = IdWorker.getId();\n        var h2UserList = List.of(new H2User(id, \"testSaveOrUpdateBatchMapper4\"), new H2User(id, \"testSaveOrUpdateBatchMapper4-1\"));\n        var mapperMethod = new MybatisBatch.Method<H2User>(H2UserMapper.class);\n        // 共享一个sqlSession,每次selectById都会刷新一下,第二条记录为update.\n        var batchResults = userMapper.insertOrUpdate(h2UserList,\n            ((sqlSession, h2User) -> sqlSession.selectList(mapperMethod.get(\"selectById\").getStatementId(), h2User.getTestId()).isEmpty()));\n        Assertions.assertTrue(SqlHelper.retBool(batchResults));\n        Assertions.assertEquals(h2UserList.size(), batchResults.stream().flatMapToInt(r -> IntStream.of(r.getUpdateCounts())).count());\n        Assertions.assertEquals(\"testSaveOrUpdateBatchMapper4-1\", userMapper.selectById(id).getName());\n    }\n\n    @Test\n    void testRemoveByIds() {\n        Assertions.assertEquals(userMapper.deleteByIds(List.of(666666661, \"2\")), userMapper.deleteByIds(List.of(666666661, \"2\"), false));\n        H2User h2User = new H2User(\"testRemoveByIds\");\n        Assertions.assertEquals(1, userMapper.insert(h2User));\n        Assertions.assertEquals(1, userMapper.deleteByIds(List.of(h2User)));\n        Assertions.assertNotNull(userMapper.getById(h2User.getTestId()).getLastUpdatedDt());\n        h2User = new H2User(\"testRemoveByIds\");\n        Assertions.assertEquals(1, userMapper.insert(h2User));\n        Assertions.assertEquals(1, userMapper.deleteByIds(List.of(h2User), false));\n        Assertions.assertNull(userMapper.getById(h2User.getTestId()).getLastUpdatedDt());\n    }\n\n    @Test\n    void testSaveOrUpdateBatch2() {\n        int batchSize = 10;\n        List<H2User> h2UserList = new ArrayList<>();\n        for (int i = 0; i < batchSize; i++) {\n            h2UserList.add(new H2User(Long.valueOf(50000 + i), \"test\" + i));\n        }\n        MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);\n        List<BatchResult> batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList,\n                mapperMethod.insert(),\n                ((sqlSession, h2User) -> sqlSession.selectList(H2UserMapper.class.getName() + \".selectById\", h2User.getTestId()).isEmpty()),\n                mapperMethod.updateById());\n        // 使用共享的sqlSession,等于每次都是刷新了,批次总结果集就等于数据大小了\n        Assertions.assertEquals(batchSize, batchResults.size());\n        for (BatchResult batchResult : batchResults) {\n            Assertions.assertEquals(1, batchResult.getUpdateCounts().length);\n            Assertions.assertEquals(1, batchResult.getUpdateCounts()[0]);\n        }\n    }\n\n    @Test\n    void testSaveOrUpdateBatch3() {\n        var id = IdWorker.getId();\n        var h2UserList = List.of(new H2User(id, \"testSaveOrUpdateBatch3\"), new H2User(id, \"testSaveOrUpdateBatch3-1\"));\n        var mapperMethod = new MybatisBatch.Method<H2User>(H2UserMapper.class);\n        // 由于没有共享一个sqlSession,第二条记录selectById的时候第一个sqlSession的数据还没提交,会执行插入导致主键冲突.\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList,\n            mapperMethod.insert(),\n            ((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null),\n            mapperMethod.updateById()));\n\n    }\n\n    @Test\n    void testSaveOrUpdateBatch4() {\n        var id = IdWorker.getId();\n        var h2UserList = List.of(new H2User(id, \"testSaveOrUpdateBatch4\"), new H2User(id, \"testSaveOrUpdateBatch4-1\"));\n        var mapperMethod = new MybatisBatch.Method<H2User>(H2UserMapper.class);\n        // 共享一个sqlSession,每次selectById都会刷新一下,第二条记录为update.\n        var batchResults = MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, h2UserList,\n            mapperMethod.insert(),\n            ((sqlSession, h2User) -> sqlSession.selectList(mapperMethod.get(\"selectById\").getStatementId(), h2User.getTestId()).isEmpty()),\n            mapperMethod.updateById());\n        var updateCounts = batchResults.getFirst().getUpdateCounts();\n        for (int updateCount : updateCounts) {\n            Assertions.assertEquals(1, updateCount);\n        }\n        Assertions.assertEquals(\"testSaveOrUpdateBatch4-1\", userMapper.selectById(id).getName());\n    }\n\n\n    @Test\n    @Order(1)\n    void crudTest() {\n        H2User h2User = new H2User();\n        h2User.setName(NQQ);\n        h2User.setAge(AgeEnum.ONE);\n        h2User.setDeleted(0);\n        h2User.setDesc(\"这是一个不错的小伙子\");\n        h2User.setTestType(1);\n        Assertions.assertEquals(1, userMapper.insert(h2User));\n\n        log(h2User.getTestId());\n\n        // 新增一条自定义 ID = 1 的测试删除数据\n        h2User.setTestId(1L);\n        h2User.setName(\"测试\");\n        userMapper.insert(h2User);\n        for (int i = 0; i < 10; i++) {\n            userMapper.insert(new H2User(\"mp\" + i, AgeEnum.ONE));\n        }\n        Assertions.assertEquals(1, userMapper.deleteById(1L));\n\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"name\", \"mp0\");\n        map.put(\"age\", AgeEnum.ONE);\n\n        // 根据 map 查询\n        h2User = userMapper.selectByMap(map).getFirst();\n        Assertions.assertSame(AgeEnum.ONE, h2User.getAge());\n\n        // 根据 map 删除\n        Assertions.assertEquals(1, userMapper.deleteByMap(map));\n\n        // 查询列表\n        LambdaQueryWrapper<H2User> wrapper = new QueryWrapper<H2User>().lambda().like(H2User::getName, \"mp\");\n        log(wrapper.getSqlSegment());\n\n        List<H2User> h2UserList = userMapper.selectList(wrapper);\n        Assertions.assertTrue(CollectionUtils.isNotEmpty(h2UserList));\n\n        // 查询总数\n        long count = userMapper.selectCount(wrapper.clone());\n        Assertions.assertTrue(count > 1);\n\n        // 批量删除\n        Assertions.assertEquals(count, userMapper.deleteByIds(h2UserList.stream().map(SuperEntity::getTestId).collect(toList())));\n\n        // 更新\n        h2User = new H2User();\n        h2User.setAge(AgeEnum.TWO);\n        h2User.setDesc(\"测试置空\");\n        Assertions.assertEquals(1, userMapper.update(h2User, new QueryWrapper<H2User>().eq(\"name\", NQQ)));\n\n        log(userMapper.selectOne(new QueryWrapper<>(new H2User().setName(NQQ).setAge(AgeEnum.TWO))));\n\n        h2User.setAge(AgeEnum.THREE);\n        h2User.setDesc(null);\n        Assertions.assertTrue(userMapper.update(h2User,\n                new UpdateWrapper<H2User>().lambda()\n                        .set(H2User::getDesc, \"\")\n                        .eq(H2User::getName, \"Jerry\")) > 0);\n\n        log(userMapper.selectOne(new QueryWrapper<>(new H2User().setName(NQQ).setAge(AgeEnum.THREE))));\n\n        Assertions.assertEquals(1, userMapper.insert(h2User));\n        // 根据主键更新 age = 18\n        h2User.setAge(AgeEnum.TWO);\n        Assertions.assertEquals(1, userMapper.updateById(h2User));\n        long testId = h2User.getTestId();\n        // https://github.com/baomidou/mybatis-plus/issues/299\n        Assertions.assertEquals(1, userMapper.updateById(new H2User() {{\n            setTestId(testId);\n            setAge(AgeEnum.TWO);\n        }}));\n        // 查询一条记录\n        Assertions.assertNotNull(userMapper.selectOne(new QueryWrapper<>(new H2User().setName(\"Joe\").setTestType(1))));\n\n        log(h2User.toString());\n\n        // 分页查询\n        IPage<H2User> h2UserPage = userMapper.selectPage(new Page<>(1, 10), null);\n        if (null != h2UserPage) {\n            System.out.println(h2UserPage.getTotal());\n            System.out.println(h2UserPage.getSize());\n        }\n        Assertions.assertNotNull(userMapper.selectPage(new Page<>(1, 10), new QueryWrapper<H2User>().orderByAsc(\"name\")));\n\n        // 查询结果集，测试 lambda 对象后 QueryWrapper 是否参数继续传递\n        QueryWrapper<H2User> qw = new QueryWrapper<>();\n        qw.lambda().eq(H2User::getName, NQQ);\n        List<Map<String, Object>> mapList = userMapper.selectMaps(qw);\n        if (CollectionUtils.isNotEmpty(mapList)) {\n            for (Map<String, Object> m : mapList) {\n                System.out.println(m);\n            }\n        }\n        Assertions.assertTrue(CollectionUtils.isNotEmpty(userMapper.selectMaps(new QueryWrapper<>(new H2User().setTestType(3)))));\n\n        // 测试自定义注入方法\n        h2User.setDesc(\"\");\n        h2User.setTestDate(new Date());\n        Assertions.assertTrue(userMapper.alwaysUpdateSomeColumnById(h2User) > 0);\n        Assertions.assertEquals(\"\", userMapper.selectById(h2User.getTestId()).getDesc());\n    }\n\n    @Test\n    void testCall() {\n        Assertions.assertEquals(\"1\", userMapper.testCall());\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    void delete() {\n        userMapper.delete(new QueryWrapper<>(new H2User().setAge(AgeEnum.TWO))\n                .eq(\"name\", \"Tony\"));\n    }\n\n    @Test\n    @Order(Integer.MAX_VALUE)\n    void sqlCommentTest() {\n        userMapper.delete(new QueryWrapper<H2User>().comment(\"deleteAllUsers\"));\n        String name = \"name1\", nameNew = \"name1New\";\n        int insertCount = userMapper.insert(new H2User().setName(name).setAge(AgeEnum.ONE));\n        Assertions.assertEquals(1, insertCount);\n        int updateCount = userMapper.update(new H2User(),\n                new UpdateWrapper<H2User>().comment(\"updateUserName1\").lambda()\n                        .set(H2User::getName, nameNew)\n                        .eq(H2User::getName, name)\n        );\n        Assertions.assertEquals(1, updateCount);\n        H2User h2User = userMapper.selectOne(\n                new QueryWrapper<H2User>().lambda().comment(\"getUserByUniqueName\")\n                        .eq(H2User::getName, nameNew)\n        );\n        Assertions.assertNotNull(h2User);\n        LambdaQueryWrapper<H2User> queryWrapper = new QueryWrapper<H2User>().lambda().ge(H2User::getAge, 1);\n        long userCount = userMapper.selectCount(queryWrapper.comment(\"getUserCount\"));\n        Assertions.assertEquals(1, userCount);\n        List<H2User> h2UserList = userMapper.selectList(queryWrapper.comment(\"getUserList\"));\n        Assertions.assertEquals(1, h2UserList.size());\n        IPage<H2User> h2UserIPage = userMapper.selectPage(new Page<>(1, 10), queryWrapper.comment(\"getUserPage\"));\n        Assertions.assertEquals(1, h2UserIPage.getRecords().size());\n        List<Map<String, Object>> selectMaps = userMapper.selectMaps(queryWrapper.comment(\"getUserMaps\"));\n        Assertions.assertEquals(1, selectMaps.size());\n        IPage<Map<String, Object>> selectMapsPage = userMapper.selectMapsPage(new Page<>(1, 10), queryWrapper.comment(\"getUserMapsPage\"));\n        Assertions.assertEquals(1, selectMapsPage.getRecords().size());\n        List<Object> selectObjs = userMapper.selectObjs(queryWrapper.comment(\"getUserObjs\"));\n        Assertions.assertEquals(1, selectObjs.size());\n    }\n\n    @Test\n    void test() {\n        Page<H2User> page = new Page<>();\n        userMapper.testPage1(new H2User(), page);\n        userMapper.testPage2(page, new H2User());\n    }\n\n    @Test\n    void testCountLong() {\n        Long count = userMapper.selectCountLong();\n        System.out.println(count);\n    }\n\n    @Test\n    void testUpdateByWrapper() {\n        var h2User = new H2User();\n        userMapper.insert(h2User);\n        var wrapper = Wrappers.<H2User>lambdaUpdate().set(H2User::getName, \"testUpdateByWrapper\").eq(H2User::getTestId, h2User.getTestId());\n        Assertions.assertEquals(1, userMapper.update(wrapper));\n        Assertions.assertEquals(\"testUpdateByWrapper\", userMapper.selectById(h2User.getTestId()).getName());\n    }\n\n    @Test\n    void testSaveOrUpdate() {\n        var h2User = new H2User();\n        userMapper.insertOrUpdate(h2User);\n        Assertions.assertNotNull(h2User.getTestId());\n        Assertions.assertNull(h2User.getLastUpdatedDt());\n        h2User.setName(\"test\");\n        userMapper.insertOrUpdate(h2User);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserStrategyTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.test.h2.entity.H2UserStrategy;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserStrategyMapper;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass H2UserStrategyTest extends BaseTest {\n\n    @Autowired\n    protected H2UserStrategyMapper userStrategyMapper;\n\n    @Test\n    @Order(1)\n    void testNewlyAdded3Strategy() {\n        H2UserStrategy insertUser = new H2UserStrategy();\n        insertUser.setName(\"userStrategy\").setDesc(\"updateStrategy=IGNORE\").setVersion(1);\n        int row = userStrategyMapper.insert(insertUser);\n        Long id = insertUser.getTestId();\n        Assertions.assertEquals(1, row);\n        Assertions.assertEquals(3, userStrategyMapper.selectById(id).getTestType(), \"autofilled with 3\");\n\n        QueryWrapper<H2UserStrategy> wrapper = new QueryWrapper<>(new H2UserStrategy().setDesc(\"updateStrategy=IGNORE\").setTestType(3));\n        Assertions.assertEquals(0, userStrategyMapper.selectCount(wrapper), \"name is whereStrategy=IGNORE, so should have where name=null which cause count=0\");\n\n        H2UserStrategy updateUser = new H2UserStrategy().setName(\"update\");\n        updateUser.setTestId(id);\n        Assertions.assertEquals(1, userStrategyMapper.updateById(updateUser));\n\n        H2UserStrategy selectUser = userStrategyMapper.selectById(id);\n        Assertions.assertEquals(\"update\", selectUser.getName());\n        Assertions.assertNull(selectUser.getDesc(), \"desc is updateStrategy=IGNORE, so should have set desc=null when updateById\");\n        Assertions.assertNull(selectUser.getTestType(), \"handle: strategy=IGNORED, should be set test_type=null when updateById \");\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.LocalDateTime;\nimport java.util.*;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.exceptions.PersistenceException;\nimport org.apache.ibatis.exceptions.TooManyResultsException;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.IdWorker;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport com.baomidou.mybatisplus.test.h2.service.IH2UserService;\n\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.select.Select;\n\n/**\n * Mybatis Plus H2 Junit Test\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass H2UserTest extends BaseTest {\n\n    @Autowired\n    protected IH2UserService userService;\n    @Autowired\n    SqlSessionFactory sqlSessionFactory;\n\n    @Autowired\n    private H2StudentMapper h2StudentMapper;\n\n    public void initBatchLimitation(int limitation) {\n        if (sqlSessionFactory instanceof DefaultSqlSessionFactory) {\n            Configuration configuration = sqlSessionFactory.getConfiguration();\n            for (Interceptor interceptor : configuration.getInterceptors()) {\n                if (interceptor instanceof MybatisPlusInterceptor) {\n                    List<InnerInterceptor> innerInterceptors = ((MybatisPlusInterceptor) interceptor).getInterceptors();\n                    for (InnerInterceptor innerInterceptor : innerInterceptors) {\n                        if (innerInterceptor instanceof DataChangeRecorderInnerInterceptor) {\n                            ((DataChangeRecorderInnerInterceptor) innerInterceptor).setBatchUpdateLimit(limitation).openBatchUpdateLimitation();\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    @Test\n    @Order(1)\n    void testInsertMy() {\n        String name = \"自定义insert\";\n        int version = 1;\n        int row = userService.myInsert(name, version);\n        Assertions.assertEquals(1, row);\n    }\n\n    @Test\n    @Order(2)\n    void testInsertObjectWithParam() {\n        String name = \"自定义insert带Param注解\";\n        int version = 1;\n        int row = userService.myInsertWithParam(name, version);\n        Assertions.assertEquals(1, row);\n    }\n\n    @Test\n    @Order(3)\n    void testInsertObjectWithoutParam() {\n        String name = \"自定义insert带Param注解\";\n        int version = 1;\n        int row = userService.myInsertWithoutParam(name, version);\n        Assertions.assertEquals(1, row);\n    }\n\n\n    @Test\n    @Order(6)\n    void testSelectLambdaById() {\n        H2User h2User = userService.getOne(Wrappers.<H2User>lambdaQuery().eq(H2User::getTestId, 101));\n        Assertions.assertNotNull(h2User);\n    }\n\n\n    @Test\n    @Order(7)\n    void testLambdaTypeHandler() {\n        // 演示 json 格式 Wrapper TypeHandler 查询\n        H2User h2User = userService.getOne(Wrappers.<H2User>lambdaQuery()\n            .apply(\"name={0,typeHandler=\" + H2userNameJsonTypeHandler.class.getCanonicalName() + \"}\",\n                \"{\\\"id\\\":101,\\\"name\\\":\\\"Tomcat\\\"}\"));\n        Assertions.assertNotNull(h2User);\n    }\n\n    @Test\n    @Order(10)\n    void testEntityWrapperSelectSql() {\n        QueryWrapper<H2User> ew = new QueryWrapper<>();\n        ew.select(\"test_id, name, age\");\n        List<H2User> list = userService.list(ew);\n        for (H2User u : list) {\n            Assertions.assertNotNull(u.getTestId());\n            Assertions.assertNotNull(u.getName());\n            Assertions.assertNull(u.getPrice());\n        }\n    }\n\n    @Test\n    @Order(10)\n    void testQueryWithParamInSelectStatement() {\n        Map<String, Object> param = new HashMap<>();\n        String nameParam = \"selectStmtParam\";\n        param.put(\"nameParam\", nameParam);\n        param.put(\"ageFrom\", 1);\n        param.put(\"ageTo\", 100);\n        List<H2User> list = userService.queryWithParamInSelectStatememt(param);\n        Assertions.assertNotNull(list);\n        for (H2User u : list) {\n            Assertions.assertEquals(nameParam, u.getName());\n            Assertions.assertNotNull(u.getTestId());\n        }\n    }\n\n    @Test\n    @Order(10)\n    void testSelectCountWithParamInSelectItems() {\n        Map<String, Object> param = new HashMap<>();\n        String nameParam = \"selectStmtParam\";\n        param.put(\"nameParam\", nameParam);\n        param.put(\"ageFrom\", 1);\n        param.put(\"ageTo\", 100);\n        int count = userService.selectCountWithParamInSelectItems(param);\n        Assertions.assertNotEquals(0, count);\n    }\n\n    @Test\n    @Order(15)\n    void testUpdateByIdWithOptLock() {\n        Long id = 991L;\n        H2User user = new H2User();\n        user.setTestId(id);\n        user.setName(\"991\");\n        user.setAge(AgeEnum.ONE);\n        user.setPrice(BigDecimal.TEN);\n        user.setDesc(\"asdf\");\n        user.setTestType(1);\n        user.setVersion(1);\n        final LocalDateTime dateTime = LocalDateTime.of(2024, 3, 29, 10, 0, 0);\n        user.setCreatedDt(dateTime);\n        userService.save(user);\n\n        H2User userDB = userService.getById(id);\n        Assertions.assertEquals(1, userDB.getVersion().intValue());\n        Assertions.assertEquals(0, userDB.getCreatedDt().compareTo(dateTime));\n\n        userDB.setName(\"992\");\n        userDB.setCreatedDt(dateTime);\n        System.out.println(\"===============================================\");\n        userService.updateById(userDB);\n        Assertions.assertEquals(2, userDB.getVersion().intValue(), \"updated version value should be updated to entity\");\n\n        userDB = userService.getById(id);\n        Assertions.assertEquals(2, userDB.getVersion().intValue());\n        Assertions.assertEquals(\"992\", userDB.getName());\n        userDB.setCreatedDt(LocalDateTime.now());\n        userService.updateById(userDB);\n        System.out.println(\"===============================================\");\n        userService.lambdaUpdate().set(H2User::getAge, AgeEnum.THREE).eq(H2User::getTestId, id).update();\n\n    }\n\n    @Test\n    @Order(16)\n    void testUpdateByEwWithOptLock() {\n        H2User userInsert = new H2User();\n        userInsert.setName(\"optLockerTest\");\n        userInsert.setAge(AgeEnum.THREE);\n        userInsert.setPrice(BigDecimal.TEN);\n        userInsert.setDesc(\"asdf\");\n        userInsert.setTestType(1);\n        userInsert.setVersion(99);\n        userService.save(userInsert);\n\n        QueryWrapper<H2User> ew = new QueryWrapper<>();\n        ew.ge(\"age\", AgeEnum.TWO.getValue());\n        Long id99 = null;\n        for (H2User u : userService.list(ew)) {\n            System.out.println(u.getName() + \",\" + u.getAge() + \",\" + u.getVersion());\n            if (u.getVersion() != null && u.getVersion() == 99) {\n                id99 = u.getTestId();\n            }\n        }\n        userService.update(new H2User().setPrice(BigDecimal.TEN).setVersion(99), ew);\n        System.out.println(\"============after update\");\n        ew = new QueryWrapper<>();\n        ew.ge(\"age\", AgeEnum.TWO.getValue());\n        for (H2User u : userService.list(ew)) {\n            System.out.println(u.getName() + \",\" + u.getAge() + \",\" + u.getVersion());\n            if (u.getTestId().equals(id99)) {\n                Assertions.assertEquals(100, u.getVersion().intValue(), \"optLocker should update version+=1\");\n            }\n        }\n    }\n\n    @Test\n    @Order(17)\n    void testOptLocker4WrapperIsNull() {\n        H2User userInsert = new H2User();\n        userInsert.setName(\"optLockerTest\");\n        userInsert.setAge(AgeEnum.THREE);\n        userInsert.setPrice(BigDecimal.TEN);\n        userInsert.setDesc(\"asdf\");\n        userInsert.setTestType(1);\n        userInsert.setVersion(99);\n        userService.save(userInsert);\n\n        QueryWrapper<H2User> ew = new QueryWrapper<>();\n        ew.ge(\"age\", AgeEnum.TWO.getValue());\n        Long id99 = null;\n        Map<Long, BigDecimal> idPriceMap = new HashMap<>();\n        for (H2User u : userService.list(ew)) {\n            System.out.println(u.getName() + \",\" + u.getAge() + \",\" + u.getVersion());\n            idPriceMap.put(u.getTestId(), u.getPrice());\n            if (u.getVersion() != null && u.getVersion() == 99) {\n                id99 = u.getTestId();\n            }\n        }\n        userService.update(new H2User().setPrice(BigDecimal.TEN).setVersion(99), null);\n        System.out.println(\"============after update\");\n        ew = new QueryWrapper<>();\n        ew.ge(\"age\", AgeEnum.TWO.getValue());\n        for (H2User u : userService.list(ew)) {\n            System.out.println(u.getName() + \",\" + u.getAge() + \",\" + u.getVersion());\n            if (u.getTestId().equals(id99)) {\n                Assertions.assertEquals(100, u.getVersion().intValue(), \"optLocker should update version+=1\");\n            } else {\n                Assertions.assertEquals(idPriceMap.get(u.getTestId()), u.getPrice(), \"other records should not be updated\");\n            }\n        }\n        userService.update(new H2User().setPrice(BigDecimal.ZERO), null);\n        for (H2User u : userService.list(new QueryWrapper<>())) {\n            System.out.println(u.getName() + \",\" + u.getAge() + \",\" + u.getVersion());\n            Assertions.assertEquals(u.getPrice().setScale(2, RoundingMode.HALF_UP).intValue(), BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP).intValue(), \"all records should be updated\");\n        }\n        try {\n            initBatchLimitation(3);\n            userService.update(new H2User().setPrice(BigDecimal.ZERO), null);\n            Assertions.fail(\"SHOULD NOT REACH HERE\");\n        } catch (Exception e) {\n            Assertions.assertTrue(checkIsDataUpdateLimitationException(e));\n        }\n    }\n\n    private boolean checkIsDataUpdateLimitationException(Throwable e) {\n        if (e instanceof DataChangeRecorderInnerInterceptor.DataUpdateLimitationException) {\n            return true;\n        }\n        if (e.getCause() == null) {\n            return false;\n        }\n        return checkIsDataUpdateLimitationException(e.getCause());\n    }\n\n    @Test\n    @Order(18)\n    void testBatchTransactional() {\n        try {\n            userService.testBatchTransactional();\n        } catch (MybatisPlusException e) {\n            List<H2User> list = userService.list(new QueryWrapper<H2User>().like(\"name\", \"batch\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(list));\n        }\n    }\n\n    @Test\n    @Order(19)\n    void testSimpleTransactional() {\n        try {\n            userService.testSimpleTransactional();\n        } catch (MybatisPlusException e) {\n            List<H2User> list = userService.list(new QueryWrapper<H2User>().like(\"name\", \"simple\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(list));\n        }\n    }\n\n    @Test\n    @Order(20)\n    void testSaveOrUpdateBatchTransactional() {\n        try {\n            userService.testSaveOrUpdateBatchTransactional();\n        } catch (MybatisPlusException e) {\n            List<H2User> list = userService.list(new QueryWrapper<H2User>().like(\"name\", \"savOrUpdate\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(list));\n        }\n    }\n\n    @Test\n    @Order(21)\n    void testSaveBatch() {\n        Assertions.assertTrue(userService.saveBatch(List.of(new H2User(\"saveBatch0\"))));\n        Assertions.assertTrue(userService.saveBatch(List.of(new H2User(\"saveBatch1\"), new H2User(\"saveBatch2\"), new H2User(\"saveBatch3\"), new H2User(\"saveBatch4\"))));\n        Assertions.assertEquals(5, userService.count(new QueryWrapper<H2User>().like(\"name\", \"saveBatch\")));\n        Assertions.assertTrue(userService.saveBatch(List.of(new H2User(\"saveBatch5\"), new H2User(\"saveBatch6\"), new H2User(\"saveBatch7\"), new H2User(\"saveBatch8\")), 2));\n        Assertions.assertEquals(9, userService.count(new QueryWrapper<H2User>().like(\"name\", \"saveBatch\")));\n    }\n\n    @Test\n    @Order(22)\n    void testUpdateBatch() {\n        Assertions.assertTrue(userService.updateBatchById(Arrays.asList(new H2User(1010L, \"batch1010\"),\n            new H2User(1011L, \"batch1011\"), new H2User(1010L, \"batch1010\"), new H2User(1012L, \"batch1012\"))));\n        Assertions.assertEquals(\"batch1010\", userService.getById(1010L).getName());\n        Assertions.assertEquals(\"batch1011\", userService.getById(1011L).getName());\n        Assertions.assertEquals(\"batch1012\", userService.getById(1012L).getName());\n        Assertions.assertTrue(userService.updateBatchById(Arrays.asList(new H2User(1010L, \"batch1010A\"),\n            new H2User(1011L, \"batch1011A\"), new H2User(1010L, \"batch1010\"), new H2User(1012L, \"batch1012\")), 1));\n        Assertions.assertEquals(\"batch1010\", userService.getById(1010L).getName());\n        Assertions.assertEquals(\"batch1011A\", userService.getById(1011L).getName());\n        Assertions.assertEquals(\"batch1012\", userService.getById(1012L).getName());\n    }\n\n    @Test\n    @Order(23)\n    void testSaveOrUpdateBatch() {\n        Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User(1010L, \"batch1010\"),\n            new H2User(\"batch1011\"), new H2User(1010L, \"batch1010\"), new H2User(\"batch1015\"))));\n        Assertions.assertEquals(\"batch1010\", userService.getById(1010L).getName());\n        Assertions.assertEquals(1, userService.count(new QueryWrapper<H2User>().eq(\"name\", \"batch1011\")));\n        Assertions.assertEquals(1, userService.count(new QueryWrapper<H2User>().eq(\"name\", \"batch1015\")));\n        Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User(1010L, \"batch1010A\"),\n            new H2User(\"batch1011AB\"), new H2User(1010L, \"batch1010\"), new H2User(\"batch1016\")), 1));\n        Assertions.assertEquals(\"batch1010\", userService.getById(1010L).getName());\n        Assertions.assertEquals(1, userService.count(new QueryWrapper<H2User>().eq(\"name\", \"batch1011AB\")));\n        Assertions.assertEquals(1, userService.count(new QueryWrapper<H2User>().eq(\"name\", \"batch1016\")));\n    }\n\n    @Test\n    @Order(24)\n    void testSimpleAndBatch() {\n        Assertions.assertTrue(userService.save(new H2User(\"testSimpleAndBatch1\", 0)));\n        Assertions.assertEquals(1, userService.count(new QueryWrapper<H2User>().eq(\"name\", \"testSimpleAndBatch1\")));\n        Assertions.assertTrue(userService.saveOrUpdateBatch(Arrays.asList(new H2User(\"testSimpleAndBatch2\"), new H2User(\"testSimpleAndBatch3\"), new H2User(\"testSimpleAndBatch4\")), 1));\n        Assertions.assertEquals(4, userService.count(new QueryWrapper<H2User>().like(\"name\", \"testSimpleAndBatch\")));\n    }\n\n    @Test\n    @Order(25)\n    void testSimpleAndBatchTransactional() {\n        try {\n            userService.testSimpleAndBatchTransactional();\n        } catch (MybatisPlusException e) {\n            List<H2User> list = userService.list(new QueryWrapper<H2User>().like(\"name\", \"simpleAndBatchTx\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(list));\n        }\n    }\n\n    @Test\n    @Order(26)\n    void testServiceImplInnerLambdaQuery() {\n        H2User tomcat = userService.lambdaQuery().eq(H2User::getName, \"Tomcat\").one();\n        Assertions.assertNotNull(tomcat);\n        Assertions.assertNotEquals(0L, userService.lambdaQuery().like(H2User::getName, \"a\").count().longValue());\n\n        List<H2User> users = userService.lambdaQuery().like(H2User::getName, \"T\")\n            .ne(H2User::getAge, AgeEnum.TWO)\n            .ge(H2User::getVersion, 1)\n            .isNull(H2User::getPrice)\n            .list();\n        Assertions.assertTrue(users.isEmpty());\n    }\n\n    @Test\n    @Order(27)\n    void testServiceChainQuery() {\n        H2User tomcat = userService.query().eq(\"name\", \"Tomcat\").one();\n        Assertions.assertNotNull(tomcat, \"tomcat should not be null\");\n        userService.query().nested(i -> i.eq(\"name\", \"Tomcat\")).list();\n        userService.lambdaUpdate().set(H2User::getName, \"Tom\").eq(H2User::getName, \"Tomcat\").update();\n    }\n\n\n    @Test\n    @Order(28)\n    void testSaveBatchException() {\n        try {\n            userService.saveBatch(Arrays.asList(\n                new H2User(1L, \"tom\"),\n                new H2User(1L, \"andy\")\n            ));\n        } catch (Exception e) {\n            Assertions.assertInstanceOf(DataAccessException.class, e);\n        }\n    }\n\n    @Test\n    @Order(29)\n    @Transactional\n    void testClearSqlSessionCache() {\n        H2User h2User;\n        h2User = userService.getById(996102919L);\n        assert h2User == null;\n        userService.saveBatch(Collections.singletonList(new H2User(996102919L, \"靓仔\")));\n        h2User = userService.getById(996102919L);\n        Assertions.assertNotNull(h2User);\n    }\n\n    @Test\n    @Order(30)\n    void testSaveBatchNoTransactional1() {\n        userService.testSaveBatchNoTransactional1();\n        Assertions.assertEquals(3, userService.count(new QueryWrapper<H2User>().like(\"name\", \"testSaveBatchNoTransactional1\")));\n    }\n\n    @Test\n    @Order(30)\n    void testSaveBatchNoTransactional2() {\n        try {\n            userService.testSaveBatchNoTransactional2();\n        } catch (Exception e) {\n            Assertions.assertEquals(3, userService.count(new QueryWrapper<H2User>().like(\"name\", \"testSaveBatchNoTransactional2\")));\n        }\n    }\n\n    @Test\n    @Order(31)\n    void testSpaceCharacter() {\n        Assertions.assertFalse(StringUtils.isNotBlank(\" \"));\n        Assertions.assertTrue(StringUtils.checkValNotNull(\" \"));\n        H2User h2User = new H2User();\n        h2User.setName(\" \");\n        Assertions.assertTrue(CollectionUtils.isEmpty(userService.list(new QueryWrapper<>(h2User)\n            .gt(\"age\", 1).lt(\"age\", 5))));\n    }\n\n    @Test\n    @Order(32)\n    void testSqlInjectionByCustomSqlSegment() {\n        // Preparing: select * from h2user WHERE (name LIKE ?)\n        // Parameters: %y%%(String)\n        List<H2User> h2Users = userService.testCustomSqlSegment(new QueryWrapper<H2User>().like(\"name\", \"y%\"));\n        Assertions.assertEquals(2, h2Users.size());\n    }\n\n    @Test\n    void myQueryWithGroupByOrderBy() {\n        userService.mySelectMaps().forEach(System.out::println);\n    }\n\n    @Test\n    void notParser() throws Exception {\n        final String targetSql1 = \"SELECT * FROM user WHERE id NOT LIKE ?\";\n        final Select select = (Select) CCJSqlParserUtil.parse(targetSql1);\n        Assertions.assertEquals(targetSql1, select.toString());\n\n        final String targetSql2 = \"SELECT * FROM user WHERE id NOT IN (?)\";\n        final Select select2 = (Select) CCJSqlParserUtil.parse(targetSql2);\n        Assertions.assertEquals(targetSql2, select2.toString());\n\n        final String targetSql3 = \"SELECT * FROM user WHERE id IS NOT NULL\";\n        final Select select3 = (Select) CCJSqlParserUtil.parse(targetSql3);\n        Assertions.assertEquals(targetSql3, select3.toString());\n    }\n\n    /**\n     * CTO 说批量插入性能不行，让我们来分析一下问题在哪里\n     */\n    @Test\n    void batchInsertPerformanceTest() {\n        List<H2User> users = mockUser(10_000, 99989);\n        userService.saveBatch(users);\n        // 卧槽，速度挺快的\n    }\n\n    /**\n     * 模拟一群人\n     *\n     * @param size     这群人的数量\n     * @param cardinal 这群人 id 的起始值\n     * @return 返回模拟的一群人\n     */\n    private List<H2User> mockUser(int size, long cardinal) {\n        return new AbstractList<>() {\n\n            @Override\n            public H2User get(int index) {\n                long id = cardinal + index + 1;\n                H2User h2User = new H2User(id, Long.toHexString(id));\n                h2User.setVersion(0);\n                h2User.setPrice(BigDecimal.ZERO);\n                return h2User;\n            }\n\n            @Override\n            public int size() {\n                return size;\n            }\n        };\n    }\n\n    @Test\n    void testSimpleWrapperClear() {\n        userService.save(new H2User(\"逗号\", AgeEnum.TWO));\n        QueryWrapper<H2User> queryWrapper = new QueryWrapper<H2User>().eq(\"name\", \"咩咩\");\n        Assertions.assertEquals(0, userService.count(queryWrapper));\n        queryWrapper.clear();\n        queryWrapper.eq(\"name\", \"逗号\");\n        Assertions.assertEquals(1, userService.count(queryWrapper));\n        UpdateWrapper<H2User> updateWrapper = new UpdateWrapper<>();\n        updateWrapper.set(\"name\", \"逗号二号\");\n        Assertions.assertFalse(userService.update(updateWrapper.eq(\"name\", \"逗号一号\")));\n        updateWrapper.clear();\n        updateWrapper.set(\"name\", \"逗号一号\");\n        Assertions.assertTrue(userService.update(updateWrapper.eq(\"name\", \"逗号\")));\n    }\n\n    @Test\n    void testLambdaWrapperClear() {\n        userService.save(new H2User(\"小红\", AgeEnum.TWO));\n        LambdaQueryWrapper<H2User> lambdaQueryWrapper = new QueryWrapper<H2User>().lambda().eq(H2User::getName, \"小宝\");\n        lambdaQueryWrapper.orderByDesc(H2User::getName);\n        Assertions.assertEquals(0, userService.count(lambdaQueryWrapper));\n        lambdaQueryWrapper.clear();\n        lambdaQueryWrapper.eq(H2User::getName, \"小红\");\n        Assertions.assertEquals(1, userService.count(lambdaQueryWrapper));\n        LambdaUpdateWrapper<H2User> lambdaUpdateWrapper = new UpdateWrapper<H2User>().lambda().set(H2User::getName, \"小红二号\");\n        Assertions.assertFalse(userService.update(lambdaUpdateWrapper.eq(H2User::getName, \"小红一号\")));\n        lambdaUpdateWrapper.clear();\n        lambdaUpdateWrapper.set(H2User::getName, \"小红一号\");\n        Assertions.assertTrue(userService.update(lambdaUpdateWrapper.eq(H2User::getName, \"小红\")));\n    }\n\n    @Test\n    void testLogicDelWithFill() {\n        H2User h2User = new H2User(\"逻辑删除(根据ID)不填充\", AgeEnum.TWO);\n        userService.save(h2User);\n        userService.removeById(h2User.getTestId());\n        Assertions.assertNull(h2User.getLastUpdatedDt());\n        h2User = new H2User(\"测试逻辑(根据实体)删除填充\", AgeEnum.TWO);\n        userService.save(h2User);\n        userService.removeById(h2User);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n    }\n\n    /**\n     * 观察 {@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils#extract(SFunction)}\n     */\n    @RepeatedTest(1000)\n    void testLambdaCache() {\n        lambdaCache();\n    }\n\n    private void lambdaCache() {\n        Wrappers.<H2User>lambdaQuery()\n            .eq(H2User::getAge, 2)\n            .eq(H2User::getName, 2)\n            .eq(H2User::getPrice, 2)\n            .getTargetSql();\n    }\n\n    @Test\n    void testRemove() {\n        //不报错即可，无需关注返回值\n        H2User h2User = new H2User(12L, \"test\");\n//        userService.removeById((short) 100);\n//        userService.removeById(100.00);\n//        userService.removeById((float) 100);\n//        userService.removeById(100);\n        userService.removeById(100000L);\n//        userService.removeById(new BigDecimal(\"100\"));\n//        userService.removeById(\"100000\");\n        userService.removeById(h2User);\n        userService.removeByIds(Arrays.asList(10000L, h2User));\n        userService.removeByIds(Arrays.asList(10000L, h2User), false);\n        h2User = new H2User(\"test\");\n        H2UserMapper h2UserMapper = (H2UserMapper) userService.getBaseMapper();\n        h2UserMapper.insert(h2User);\n        h2UserMapper.deleteById(h2User.getTestId());\n        h2User = h2UserMapper.getById(h2User.getTestId());\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n\n        h2User = new H2User(\"test\");\n        h2UserMapper.insert(h2User);\n        h2UserMapper.deleteById(h2User.getTestId(), false);\n        h2User = h2UserMapper.getById(h2User.getTestId());\n        Assertions.assertNull(h2User.getLastUpdatedDt());\n\n        h2User = new H2User(\"test\");\n        h2UserMapper.insert(h2User);\n        h2UserMapper.deleteById(String.valueOf(h2User.getTestId()));\n        h2User = h2UserMapper.getById(h2User.getTestId());\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n    }\n\n    @Test\n    void testPageOrderBy() {\n        // test https://gitee.com/baomidou/mybatis-plus/issues/I4BGE2\n        Page<H2User> page = Page.of(1, 10);\n        Assertions.assertTrue(userService.page(page, Wrappers.<H2User>query().select(\"test_id,name\")\n            .orderByDesc(\"test_id\")).getPages() > 0);\n        Assertions.assertTrue(userService.page(page, Wrappers.<H2User>lambdaQuery()\n            .orderByDesc(H2User::getTestId)).getPages() > 0);\n    }\n\n    @Test\n    void testPageNegativeSize() {\n        Page<H2User> page = Page.of(1, -1);\n        userService.lambdaQuery().page(page);\n        Assertions.assertEquals(0, page.getTotal());\n        Assertions.assertEquals(userService.lambdaQuery().list(Page.of(1, -1, false)).size(), page.getRecords().size());\n    }\n\n    @Test\n    void testDeleteByFill() {\n        H2User h2User = new H2User(3L, \"test\");\n        userService.removeById(1L);\n        userService.removeById(1L, true);\n        userService.removeById(1, true);\n        userService.removeById(\"1\", true);\n        userService.removeById(1L, false);\n        userService.removeById(h2User);\n        userService.removeById(h2User, true);\n        userService.removeById(h2User, false);\n        userService.removeBatchByIds(Arrays.asList(1L, 2L, h2User));\n    }\n\n    @Test\n    @Order(25)\n    void testServiceImplInnerLambdaQueryConstructorSetEntity() {\n        H2User condition = new H2User();\n        condition.setName(\"Tomcat\");\n        H2User user = userService.lambdaQuery(condition).one();\n        Assertions.assertNotNull(user);\n        Assertions.assertEquals(\"Tomcat\", user.getName());\n        H2User h2User = userService.lambdaQuery().setEntity(condition).one();\n        Assertions.assertNotNull(h2User);\n        Assertions.assertEquals(\"Tomcat\", h2User.getName());\n    }\n\n    @Test\n    @Order(26)\n    void testServiceGetOptById() {\n        H2User user = new H2User(1L, \"Evan\");\n        userService.save(user);\n        Optional<H2User> optional = userService.getOptById(1L);\n        optional.ifPresent(u -> log(u.toString()));\n    }\n\n    @Test\n    @Order(27)\n    void testServiceGetOneOpt() {\n        userService.getOneOpt(Wrappers.<H2User>lambdaQuery().eq(H2User::getName, \"David\"))\n            .ifPresent(u -> log(u.toString()));\n    }\n\n    @Test\n    @Order(28)\n    void testServiceGetOneOptThrowEx() {\n        userService.getOneOpt(new LambdaQueryWrapper<H2User>().eq(H2User::getName, \"test1\"), false)\n            .ifPresent(u -> log(u.toString()));\n\n        userService.getOneOpt(new LambdaQueryWrapper<H2User>().eq(H2User::getName, \"test\"), false)\n            .ifPresent(u -> log(u.toString()));\n\n        // 异常情况\n        Assertions.assertThrows(TooManyResultsException.class, () -> userService.getOneOpt(Wrappers.<H2User>lambdaQuery()\n            .like(H2User::getName, \"tes\")));\n    }\n\n    @Test\n    void testInsertFill() {\n        H2User h2User;\n        h2User = new H2User(\"insertFillByCustomMethod1\", AgeEnum.ONE);\n        h2StudentMapper.insertFillByCustomMethod1(h2User);\n        Assertions.assertNotNull(h2User.getTestType());\n\n        h2User = new H2User(\"insertFillByCustomMethod2\", AgeEnum.ONE);\n        h2StudentMapper.insertFillByCustomMethod2(h2User);\n        Assertions.assertNotNull(h2User.getTestType());\n\n        h2User = new H2User(\"insertFillByCustomMethod3\", AgeEnum.ONE);\n        h2StudentMapper.insertFillByCustomMethod3(h2User, \"fillByCustomMethod3\");\n        Assertions.assertNotNull(h2User.getTestType());\n\n        List<H2User> list;\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod4-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod4-2\", AgeEnum.ONE));\n        h2StudentMapper.insertFillByCustomMethod4(list);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod5-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod5-2\", AgeEnum.ONE));\n        h2StudentMapper.insertFillByCustomMethod5(list);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod6-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod6-2\", AgeEnum.ONE));\n        h2StudentMapper.insertFillByCustomMethod6(list);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod7-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod7-2\", AgeEnum.ONE));\n        h2StudentMapper.insertFillByCustomMethod7(list);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        H2User[] h2Users;\n        h2Users = new H2User[]{new H2User(\"insertFillByCustomMethod8-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod8-2\", AgeEnum.ONE)};\n        h2StudentMapper.insertFillByCustomMethod8(h2Users);\n        Arrays.stream(h2Users).forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        h2Users = new H2User[]{new H2User(\"insertFillByCustomMethod9-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod9-2\", AgeEnum.ONE)};\n        h2StudentMapper.insertFillByCustomMethod9(h2Users);\n        Arrays.stream(h2Users).forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        Map<String, Object> map;\n        h2User = new H2User(\"insertFillByCustomMethod10\", AgeEnum.ONE);\n        map = new HashMap<>();\n        map.put(\"et\", h2User);\n        h2StudentMapper.insertFillByCustomMethod10(map);\n        Assertions.assertNotNull(h2User.getTestType());\n\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod11-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod11-2\", AgeEnum.ONE));\n        map = new HashMap<>();\n        map.put(\"list\", list);\n        h2StudentMapper.insertFillByCustomMethod11(map);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        list = Arrays.asList(new H2User(\"insertFillByCustomMethod12-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod12-2\", AgeEnum.ONE));\n        map = new HashMap<>();\n        map.put(\"coll\", list);\n        h2StudentMapper.insertFillByCustomMethod12(map);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n\n        h2Users = new H2User[]{new H2User(\"insertFillByCustomMethod13-1\", AgeEnum.ONE), new H2User(\"insertFillByCustomMethod13-2\", AgeEnum.ONE)};\n        map = new HashMap<>();\n        map.put(\"array\", h2Users);\n        h2StudentMapper.insertFillByCustomMethod13(map);\n        list.forEach(user -> Assertions.assertNotNull(user.getTestType()));\n    }\n\n    @Test\n    void testUpdateFill() {\n        Map<String, Object> map;\n        H2User h2User;\n        h2User = new H2User();\n        map = new HashMap<>();\n        map.put(\"et\", h2User);\n        map.put(\"list\", Arrays.asList(1L, 2L, 3L));\n        h2StudentMapper.updateFillByCustomMethod1(map);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n\n        h2User = new H2User();\n        h2StudentMapper.updateFillByCustomMethod2(Arrays.asList(1L, 2L, 3L), h2User);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n\n        h2User = new H2User();\n        h2StudentMapper.updateFillByCustomMethod3(Arrays.asList(1L, 2L, 3L), h2User);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n\n        h2User = new H2User();\n        h2StudentMapper.updateFillByCustomMethod4(Arrays.asList(1L, 2L, 3L), h2User);\n        Assertions.assertNotNull(h2User.getLastUpdatedDt());\n\n    }\n\n    @Test\n    void testListMapsByPage() {\n        Assertions.assertEquals(userService.listMaps().size(), userService.count());\n        Assertions.assertEquals(userService.listMaps(new Page<>(1, 2)).size(), userService.page(new Page<>(1, 2)).getRecords().size());\n        Assertions.assertEquals(userService.listMaps(new Page<>(2, 2)).size(), userService.page(new Page<>(2, 2)).getRecords().size());\n\n        Assertions.assertEquals(\n            userService.pageMaps(new Page<>(1, 2, false)).getRecords().size(),\n            userService.listMaps(new Page<>(1, 2, false)).size()\n        );\n        Assertions.assertEquals(\n            userService.pageMaps(new Page<>(2, 2, false)).getRecords().size(),\n            userService.listMaps(new Page<>(2, 2, false)).size()\n        );\n\n        Assertions.assertEquals(\n            userService.pageMaps(new Page<>(1, 2, false), Wrappers.emptyWrapper()).getRecords().size(),\n            userService.listMaps(new Page<>(1, 2, false), Wrappers.emptyWrapper()).size()\n        );\n        Assertions.assertEquals(\n            userService.pageMaps(new Page<>(2, 2, false), Wrappers.emptyWrapper()).getRecords().size(),\n            userService.listMaps(new Page<>(2, 2, false), Wrappers.emptyWrapper()).size()\n        );\n    }\n\n    @Test\n    void testListByPage() {\n        Assertions.assertEquals(userService.list().size(), userService.count());\n        Assertions.assertEquals(userService.list(new Page<>(1, 2)).size(), userService.page(new Page<>(1, 2)).getRecords().size());\n        Assertions.assertEquals(userService.list(new Page<>(2, 2)).size(), userService.page(new Page<>(2, 2)).getRecords().size());\n        Assertions.assertEquals(\n            userService.list(new Page<>(1, 2, false), Wrappers.emptyWrapper()).size(),\n            userService.page(new Page<>(1, 2, false), Wrappers.emptyWrapper()).getRecords().size()\n        );\n\n        List<H2User> list = userService.list(new Page<>(2, 2, false));\n\n        Assertions.assertEquals(\n            userService.list(new Page<>(2, 2, false), Wrappers.emptyWrapper()).size(),\n            userService.page(new Page<>(2, 2, false), Wrappers.emptyWrapper()).getRecords().size()\n        );\n    }\n\n    @Test\n    void testUnchecked() {\n        Wrappers.<H2User>lambdaQuery()\n            .select(H2User::getAge, H2User::getAge).select(true, H2User::getDeleted, H2User::getDeleted)\n            .orderBy(true, true, H2User::getAge, H2User::getAge)\n            .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType)\n            .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType)\n            .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType);\n\n        new LambdaQueryChainWrapper<>(H2User.class)\n            .select(H2User::getAge).select(true, H2User::getDeleted, H2User::getDeleted)\n            .orderBy(true, true, H2User::getAge, H2User::getAge)\n            .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType)\n            .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType)\n            .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType);\n\n        // 重写方法保留支持.\n        new LambdaQueryChainWrapper<>(H2User.class) {\n            @Override\n            protected LambdaQueryChainWrapper<H2User> doOrderByDesc(boolean condition, SFunction<H2User, ?> column, List<SFunction<H2User, ?>> columns) {\n                System.out.println(\"-------处理OrderByDesc----------\");\n                return super.doOrderByDesc(condition, column, columns);\n            }\n\n            @Override\n            protected LambdaQueryChainWrapper<H2User> doOrderByAsc(boolean condition, SFunction<H2User, ?> column, List<SFunction<H2User, ?>> columns) {\n                System.out.println(\"-------处理OrderByAsc----------\");\n                return super.doOrderByAsc(condition, column, columns);\n            }\n\n            @Override\n            protected LambdaQueryChainWrapper<H2User> doOrderBy(boolean condition, boolean isAsc, SFunction<H2User, ?> column, List<SFunction<H2User, ?>> columns) {\n                System.out.println(\"-------处理OrderBy----------\");\n                return super.doOrderBy(condition, isAsc, column, columns);\n            }\n\n            @Override\n            protected LambdaQueryChainWrapper<H2User> doGroupBy(boolean condition, SFunction<H2User, ?> column, List<SFunction<H2User, ?>> columns) {\n                System.out.println(\"-------处理GroupBy----------\");\n                return super.doGroupBy(condition, column, columns);\n            }\n\n            @Override\n            protected LambdaQueryChainWrapper<H2User> doSelect(boolean condition, List<SFunction<H2User, ?>> columns) {\n                System.out.println(\"-------处理Select----------\");\n                return super.doSelect(condition, columns);\n            }\n        }\n            .select(H2User::getAge)\n            .select(true, H2User::getDeleted, H2User::getDeleted)\n            .orderBy(true, true, H2User::getAge, H2User::getAge)\n            .orderByAsc(H2User::getAge, H2User::getDeleted).orderByAsc(true, H2User::getAge, H2User::getTestType)\n            .orderByDesc(H2User::getDeleted, H2User::getPrice).orderByDesc(true, H2User::getDeleted, H2User::getTestType)\n            .groupBy(H2User::getAge, H2User::getTestType).groupBy(true, H2User::getAge, H2User::getTestType);\n    }\n\n    @Test\n    void testSelectObjs() {\n        for (Object o : userService.listObjs()) {\n            Assertions.assertEquals(Long.class, o.getClass());\n        }\n        for (Long id : userService.<Long>listObjs()) {\n            System.out.println(id);\n        }\n    }\n\n    @Test\n    void testResultSet() {\n        BaseMapper<H2User> baseMapper = userService.getBaseMapper();\n        Page<H2User> page = new Page<>(1, 1000000);\n        System.out.println(\"--------------------------------------------\");\n        baseMapper.selectList(page, Wrappers.emptyWrapper());\n        List<Long> ids = new ArrayList<>();\n        System.out.println(\"---------------selectListByPage-------------------\");\n        baseMapper.selectList(page, Wrappers.emptyWrapper(), resultContext -> {\n            H2User resultObject = resultContext.getResultObject();\n            ids.add(resultObject.getTestId());\n            System.out.println(resultObject);\n        });\n        System.out.println(\"---------------selectBatchIds-------------------\");\n        baseMapper.selectByIds(ids, resultContext -> System.out.println(resultContext.getResultObject()));\n        System.out.println(\"---------------selectList-------------------\");\n        System.out.println(\"---------------selectObjs-------------------\");\n        baseMapper.selectObjs(Wrappers.emptyWrapper(), (ResultHandler<Long>) resultContext -> System.out.println(resultContext.getResultObject()));\n        System.out.println(\"---------------selectByMap-------------------\");\n        baseMapper.selectByMap(new HashMap<>(), resultContext -> System.out.println(resultContext.getResultObject()));\n        System.out.println(\"---------------selectMapsByPage-------------------\");\n        baseMapper.selectMaps(Page.of(1, 100000), Wrappers.emptyWrapper(), resultContext -> resultContext.getResultObject().forEach((k, v) -> System.out.println(k + \"--------\" + v)));\n        System.out.println(\"---------------selectMaps-------------------\");\n        baseMapper.selectMaps(Wrappers.emptyWrapper(), resultContext -> resultContext.getResultObject().forEach((k, v) -> System.out.println(k + \"--------\" + v)));\n    }\n\n    @Test\n    void testSelectOne() {\n        Assertions.assertTrue(userService.list().size() > 2);\n        Assertions.assertThrows(TooManyResultsException.class, () -> userService.getBaseMapper().selectOne(Wrappers.emptyWrapper()));\n        Assertions.assertNotNull(userService.getBaseMapper().selectOne(Wrappers.emptyWrapper(), false));\n    }\n\n    @Test\n    void testSaveOrUpdateTransactional1() {\n        var id = IdWorker.getId();\n        var userList = List.of(new H2User(id, \"test-1\"), new H2User(IdWorker.getId(), \"test-2\"), new H2User(id, \"test-3\"));\n        Assertions.assertThrowsExactly(PersistenceException.class, () -> userService.testSaveOrUpdateTransactional1(userList));\n    }\n\n    @Test\n    void testSaveOrUpdateTransactional2() {\n        var id = IdWorker.getId();\n        var userList = List.of(new H2User(id, \"test-1\"), new H2User(IdWorker.getId(), \"test-2\"), new H2User(id, \"test-3\"));\n        userService.testSaveOrUpdateTransactional2(userList);\n        Assertions.assertEquals(\"test-3\", userService.getById(id).getName());\n    }\n\n    @Test\n    void testOrderByExpression() {\n        Page<H2User> page = new Page<>();\n        page.addOrder(OrderItem.withExpression(\"\"\"\n            CASE\n              WHEN age > 1 THEN 2\n              WHEN age < 1 THEN 1\n            END\n            \"\"\"));\n        page.addOrder(OrderItem.withExpression(\"\"\"\n            CASE\n              WHEN name IS NOT NULL THEN 0\n              ELSE 1\n            END\n            \"\"\", false));\n        Assertions.assertDoesNotThrow(() -> userService.page(page));\n    }\n\n    @Test\n    @Transactional\n    void selectUsersWithCursor() {\n        // 使用 try-with-resources 确保 Cursor 被正确关闭\n        // 注意：必须添加 @Transactional 注解，保证 SqlSession 在迭代期间保持打开状态\n        try (Cursor<H2User> cursor = userService.getBaseMapper().selectWithCursor(null)) {\n\n            // 遍历游标，逐条处理数据\n            Iterator<H2User> iterator = cursor.iterator();\n            while (iterator.hasNext()) {\n                // 每次读取一条数据\n                H2User user = iterator.next();\n\n                // 处理单条记录\n                System.out.println(\"Processing user: \" + user.getName());\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        H2User user = userService.getBaseMapper().selectOne(null);\n        Assertions.assertNotNull(user);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2userNameJsonTypeHandler.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.alibaba.fastjson.JSON;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * 只做演示其它方法未实现\n */\n@Slf4j\n@MappedTypes({Object.class})\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class H2userNameJsonTypeHandler extends BaseTypeHandler<String> {\n\n    @Override\n    public void setNonNullParameter(PreparedStatement ps, int i, String json, JdbcType jdbcType) throws SQLException {\n        H2User h2User = JSON.parseObject(json, H2User.class);\n        ps.setString(i, h2User.getName());\n    }\n\n    @Override\n    public String getNullableResult(ResultSet resultSet, String s) throws SQLException {\n        return null;\n    }\n\n    @Override\n    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {\n        return null;\n    }\n\n    @Override\n    public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {\n        return null;\n    }\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/LastSqlTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * OrderBy LastSql 混合测试\n *\n * @author Dervish\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\npublic class LastSqlTest {\n\n    @Autowired\n    private H2StudentMapper mapper;\n\n    @Test\n    @Order(1)\n    public void delete() {\n        int res = mapper.delete(new LambdaUpdateWrapper<H2Student>().last(\" and 1 = 2 \").eq(H2Student::getId, 0));\n        Assertions.assertTrue(res <= 0);\n    }\n\n    @Test\n    @Order(2)\n    public void selectCount() {\n        long result = mapper.selectCount(new LambdaQueryWrapper<H2Student>().last(\"where 1 =2\"));\n        Assertions.assertTrue(result <= 0);\n    }\n\n    @Test\n    @Order(3)\n    public void selectList() {\n        List<H2Student> h2Students = mapper.selectList(new LambdaQueryWrapper<H2Student>().last(\"limit 1\"));\n        Assertions.assertEquals(1, h2Students.size());\n    }\n\n    @Test\n    @Order(4)\n    public void selectMaps() {\n        List<Map<String, Object>> maps = mapper.selectMaps(new LambdaQueryWrapper<H2Student>().last(\"limit 1\"));\n        Assertions.assertEquals(1, maps.size());\n    }\n\n    @Test\n    @Order(5)\n    public void selectMapsPage() {\n        IPage<Map<String,Object>> page = Page.of(0, 10);\n        mapper.selectMapsPage(page, new QueryWrapper<H2Student>().last(\" /* testSql */ \").comment(\"test\"));\n    }\n\n    @Test\n    @Order(6)\n    public void selectObjs() {\n        List<Object> objects = mapper.selectObjs(new QueryWrapper<H2Student>().last(\" limit 1\"));\n        Assertions.assertEquals(1, objects.size());\n    }\n\n    @Test\n    @Order(7)\n    public void SelectOne() {\n        H2Student h2Student = mapper.selectOne(new QueryWrapper<H2Student>().last(\" where 1 = 2\"));\n        Assertions.assertNull(h2Student);\n    }\n\n    @Test\n    @Order(8)\n    public void selectPage() {\n        IPage<H2Student> page = Page.of(0, 10);\n        mapper.selectPage(page, new QueryWrapper<H2Student>().last(\" /* testSql */ \"));\n    }\n\n    @Test\n    @Order(9)\n    public void update() {\n        int res = mapper.update(null, new UpdateWrapper<H2Student>().set(\"name\", \"dog\").last(\" where 1 =2 \"));\n        Assertions.assertTrue(res <= 0);\n    }\n\n    @Test\n    @Order(10)\n    public void selectListOrderBy() {\n        List<H2Student> h2Students = mapper.selectList(null);\n        Assertions.assertEquals(6, h2Students.size());\n        Assertions.assertEquals(6, mapper.selectList(new LambdaQueryWrapper<H2Student>()\n            .eq(H2Student::getAge, 1)).size());\n        Assertions.assertEquals(6, mapper.selectList(new QueryWrapper<H2Student>()\n            .orderByAsc(\"age\")).size());\n    }\n\n    @Test\n    @Order(11)\n    public void selectPageOrderBy() {\n        mapper.selectPage(Page.of(0, 10), null);\n    }\n\n    @Test\n    @Order(12)\n    public void selectMapsOrderBy() {\n        List<Map<String, Object>> maps = mapper.selectMaps(null);\n        Assertions.assertEquals(6, maps.size());\n    }\n\n    @Test\n    @Order(12)\n    public void selectMapsPageOrderBy() {\n        mapper.selectMapsPage(Page.of(0, 10), null);\n    }\n\n    @Test\n    @Order(12)\n    public void selectObjsOrderBy() {\n        List<Object> objs = mapper.selectObjs(null);\n        Assertions.assertEquals(6, objs.size());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/MybatisMapperRegistryTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.MybatisMapperRegistry;\nimport com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport com.baomidou.mybatisplus.test.h2.config.DBConfig;\nimport com.baomidou.mybatisplus.test.h2.config.MybatisPlusConfig;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2019/4/12.\n */\n@ExtendWith(SpringExtension.class)\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@MapperScan(value = \"com.baomidou.mybatisplus.test.h2\")\n@ContextConfiguration(classes = {MybatisMapperRegistryTest.class, DBConfig.class})\nclass MybatisMapperRegistryTest extends BaseTest {\n\n    private interface H2StudentChildrenMapper extends H2StudentMapper {\n\n    }\n\n    @Bean\n    DBConfig dbConfig() {\n        return new DBConfig();\n    }\n\n    @Bean\n    MybatisPlusConfig mybatisPlusConfig() {\n        return new MybatisPlusConfig();\n    }\n\n    @Bean\n    SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Autowired\n    private SqlSessionFactory sqlSessionFactory;\n\n    @SuppressWarnings(\"unchecked\")\n    @Test\n    void test() throws ReflectiveOperationException {\n        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {\n            Configuration configuration = sqlSessionFactory.getConfiguration();\n            MybatisMapperRegistry mapperRegistry = (MybatisMapperRegistry) sqlSessionFactory.getConfiguration().getMapperRegistry();\n            Assertions.assertTrue(mapperRegistry.hasMapper(H2UserMapper.class));\n            Assertions.assertTrue(mapperRegistry.hasMapper(H2StudentChildrenMapper.class));\n            H2StudentMapper studentMapper = mapperRegistry.getMapper(H2StudentMapper.class, sqlSession);\n\n            Assertions.assertTrue(configuration.hasStatement(H2StudentMapper.class.getName() + \".selectById\"));\n            studentMapper.selectById(1);\n\n            Field field = mapperRegistry.getClass().getDeclaredField(\"knownMappers\");\n            field.setAccessible(true);\n            Map<Class<?>, MybatisMapperProxyFactory<?>> knownMappers = (Map<Class<?>, MybatisMapperProxyFactory<?>>) field.get(mapperRegistry);\n            MybatisMapperProxyFactory<?> mybatisMapperProxyFactory = knownMappers.get(H2StudentChildrenMapper.class);\n\n\n            H2StudentChildrenMapper h2StudentChildrenMapper = mapperRegistry.getMapper(H2StudentChildrenMapper.class, sqlSession);\n            Assertions.assertTrue(configuration.hasStatement(H2StudentChildrenMapper.class.getName() + \".selectById\"));\n            Map<Method, ?> methodCache = mybatisMapperProxyFactory.getMethodCache();\n            Assertions.assertTrue(methodCache.isEmpty());\n\n            h2StudentChildrenMapper.selectById(2);\n            methodCache = mybatisMapperProxyFactory.getMethodCache();\n            Assertions.assertFalse(methodCache.isEmpty());\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/SqlRunnerTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport com.baomidou.mybatisplus.test.h2.service.IH2StudentService;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * SqlRunner测试\n *\n * @author nieqiurong 2018/8/25 11:05.\n */\n@ExtendWith(SpringExtension.class)\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-h2.xml\"})\nclass SqlRunnerTest {\n\n    @Autowired\n    private IH2StudentService studentService;\n\n    @Test\n    @Order(3)\n    void testSelectCount() {\n        long count = SqlRunner.db().selectCount(\"select count(1) from h2student\");\n        Assertions.assertTrue(count > 0);\n        count = SqlRunner.db().selectCount(\"select count(1) from h2student where id > {0}\", 0);\n        Assertions.assertTrue(count > 0);\n        count = SqlRunner.db(H2Student.class).selectCount(\"select count(1) from h2student\");\n        Assertions.assertTrue(count > 0);\n        count = SqlRunner.db(H2Student.class).selectCount(\"select count(1) from h2student where id > {0}\", 0);\n        Assertions.assertTrue(count > 0);\n    }\n\n    @Test\n    @Transactional\n    @Order(1)\n    void testInsert() {\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )\", \"测试学生\", 2));\n        Assertions.assertTrue(SqlRunner.db(H2Student.class).insert(\"INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )\", \"测试学生2\", 3));\n    }\n\n    @Test\n    @Order(2)\n    void testTransactional() {\n        try {\n            studentService.testSqlRunnerTransactional();\n        } catch (RuntimeException e) {\n            List<H2Student> list = studentService.list(new QueryWrapper<H2Student>().like(\"name\", \"sqlRunnerTx\"));\n            Assertions.assertTrue(CollectionUtils.isEmpty(list));\n        }\n    }\n\n    @Test\n    @Order(4)\n    void testSelectPage() {\n        IPage<Map<String,Object>> page1 = SqlRunner.db().selectPage(new Page<>(1, 3), \"select * from h2student\");\n        Assertions.assertEquals(3, page1.getRecords().size());\n        IPage<Map<String,Object>> page2 = SqlRunner.db().selectPage(new Page<>(1, 3), \"select * from h2student where id >= {0}\", 0);\n        Assertions.assertEquals(3, page2.getRecords().size());\n        IPage<Map<String,Object>> page3 = SqlRunner.db().selectPage(new Page<>(1, 3), \"select * from h2student where id = {0}\", 10086);\n        Assertions.assertEquals(0, page3.getRecords().size());\n    }\n\n    @Test\n    @Order(5)\n    void testInsertByDisorderParameter() {\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {3}, {2}, {1} )\", \"测试学生\", 2, \"'六翻了'\", 10000));\n        Assertions.assertTrue(SqlRunner.db(H2Student.class).insert(\"INSERT INTO h2student ( name, age, id ) VALUES ( {0}, {1}, {2} )\", \"测试学生2\", 3, 10001));\n        Assertions.assertEquals(2, SqlRunner.db().selectCount(\"select count(1) from h2student where (id = 10000 or id = 10001)\"));\n    }\n\n    @Test\n    @Order(6)\n    void testSpecialParameters() {\n        var name = \"`测`的'的'\\\\//塞'2\";\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {3}, {0}, {1} )\", name, 2, \"'六翻了'\", 10004));\n        Assertions.assertEquals(10004L, SqlRunner.db().selectObj(\"select id from h2student where name = {0}\", name));\n        name = \"`测`的'的'\\\\//塞'2\" + \"2\";\n        Assertions.assertTrue(SqlRunner.db().update(\"update h2student set name = {0} where id = {1}\", name, 10004L));\n        Assertions.assertEquals(10004L, SqlRunner.db().selectObj(\"select id from h2student where name = {0}\", name));\n    }\n\n    @Test\n    @Order(7)\n    void testByMap() {\n        var map = Map.of(\"name\", \"test\", \"age\", AgeEnum.TWO, \"id\", 11000L);\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )\", map));\n        Map<String, Object> resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {id}\", map);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(map.get(\"name\"), resultMap.get(\"NAME\"));\n        Assertions.assertEquals(AgeEnum.TWO.getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(map.get(\"id\"), resultMap.get(\"ID\"));\n        map = new HashMap<>();\n        map.put(\"name\",\"test\");\n        map.put(\"age\", AgeEnum.TWO);\n        map.put(\"id\", 11100L);\n        map.put(\"size\", \"测试size\");\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {id}, {size}, {age} )\", map));\n        resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {id}\", map);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(map.get(\"size\"), resultMap.get(\"NAME\"));\n        Assertions.assertEquals(AgeEnum.TWO.getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(map.get(\"id\"), resultMap.get(\"ID\"));\n    }\n\n    @Test\n    @Order(8)\n    void testByEntity() {\n        var entity = new H2Student();\n        entity.setId(11001L);\n        entity.setName(\"test\");\n        entity.setAge(12);\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )\", entity));\n    }\n\n    @Data\n    @AllArgsConstructor\n    static class StudentDto {\n\n        private Long id;\n\n        private String name;\n\n        private AgeEnum age;\n    }\n\n    @Test\n    @Order(9)\n    void testByDto() {\n        var studentDto = new StudentDto(11002L, \"测试学生\", AgeEnum.THREE);\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )\", studentDto));\n        Map<String, Object> resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {id}\", studentDto);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(studentDto.getName(), resultMap.get(\"NAME\"));\n        Assertions.assertEquals(studentDto.getAge().getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(studentDto.getId(), resultMap.get(\"ID\"));\n    }\n\n    @Test\n    @Order(10)\n    void testByArray() {\n        var array = new Object[]{11003L, \"测试学生\", AgeEnum.THREE};\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {0}, {1}, {2} )\", array));\n        Map<String, Object> resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {0}\", array);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(\"测试学生\", resultMap.get(\"NAME\"));\n        Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get(\"AGE\"));\n        resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {0}\", new long[]{11003L});\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(\"测试学生\", resultMap.get(\"NAME\"));\n        Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(11003L, resultMap.get(\"ID\"));\n        Assertions.assertNull(SqlRunner.db().selectOne(\"select * from h2student where id = {0} and name= {1}\", (Object) new Object[]{11003L, \"234\"}));\n        Assertions.assertNull(SqlRunner.db().selectOne(\"select * from h2student where id = {0} and name= {1}\", new Object[]{11003L, \"234\"}));\n        Assertions.assertNull(SqlRunner.db().selectOne(\"select * from h2student where id = {0} and name= {1}\", 11003L, \"234\"));\n    }\n\n    @Test\n    @Order(11)\n    void testByList() {\n        var list = List.of(11004L, \"测试学生\", AgeEnum.THREE);\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {0}, {1}, {2} )\", list));\n        Map<String, Object> resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {0}\", list);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(\"测试学生\", resultMap.get(\"NAME\"));\n        Assertions.assertEquals(AgeEnum.THREE.getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(11004L, resultMap.get(\"ID\"));\n    }\n\n    record StudentDtoRecord(Long id, String name, AgeEnum age) {\n\n    }\n\n    @Test\n    @Order(12)\n    void testByRecord() {\n        var studentDto = new StudentDtoRecord(11005L, \"测试学生\", AgeEnum.THREE);\n        Assertions.assertTrue(SqlRunner.db().insert(\"INSERT INTO h2student (id, name, age ) VALUES ( {id}, {name}, {age} )\", studentDto));\n        Map<String, Object> resultMap = SqlRunner.db().selectOne(\"select * from h2student where id = {id}\", studentDto);\n        Assertions.assertNotNull(resultMap);\n        Assertions.assertEquals(studentDto.name, resultMap.get(\"NAME\"));\n        Assertions.assertEquals(studentDto.age.getValue(), resultMap.get(\"AGE\"));\n        Assertions.assertEquals(studentDto.id, resultMap.get(\"ID\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/TestXmlConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.service.IH2UserService;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * 测试XML配置\n * @author nieqiurong 2018/8/14 13:30.\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-test-xml-h2.xml\"})\nclass TestXmlConfig {\n\n    @Autowired\n    protected IH2UserService userService;\n\n    @Test\n    void test() {\n        H2User user = userService.getById(101L);\n        Assertions.assertNotNull(user);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CacheConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.cache.mapper\")\npublic class CacheConfig {\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        configuration.setCacheEnabled(true);\n        sqlSessionFactory.setConfiguration(configuration);\n        MybatisPlusInterceptor pagination = new MybatisPlusInterceptor();\n        pagination.addInnerInterceptor(new PaginationInnerInterceptor());\n\n        sqlSessionFactory.setPlugins(pagination);\n        return sqlSessionFactory.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CacheTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.cache.mapper.CacheMapper;\nimport com.baomidou.mybatisplus.test.h2.cache.model.CacheModel;\nimport com.baomidou.mybatisplus.test.h2.cache.service.ICacheService;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.*;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.Arrays;\nimport java.util.Collections;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-cache-h2.xml\"})\nclass CacheTest {\n\n    @Autowired\n    private ICacheService cacheService;\n\n    @Autowired\n    private SqlSessionFactory sqlSessionFactory;\n\n    @Test\n    @Order(1)\n    void testPageCache() {\n        Cache cache = getCache();\n        IPage<CacheModel> cacheModelIPage1 = cacheService.page(new Page<>(1, 3), new QueryWrapper<>());\n        IPage<CacheModel> cacheModelIPage2 = cacheService.page(new Page<>(1, 3), new QueryWrapper<>());\n        Assertions.assertEquals(2, cache.getSize());\n        Assertions.assertEquals(cacheModelIPage1.getTotal(), cacheModelIPage2.getTotal());\n        Assertions.assertEquals(cacheModelIPage1.getRecords().size(), cacheModelIPage2.getRecords().size());\n        IPage<CacheModel> cacheModelIPage3 = cacheService.page(new Page<>(2, 3), new QueryWrapper<>());\n        Assertions.assertEquals(cacheModelIPage1.getTotal(), cacheModelIPage3.getTotal());\n        Assertions.assertEquals(2, cacheModelIPage3.getRecords().size());\n        Assertions.assertEquals(3, cache.getSize());\n        IPage<CacheModel> cacheModelIPage4 = cacheService.page(new Page<>(2, 3, false), new QueryWrapper<>());\n        Assertions.assertEquals(0L, cacheModelIPage4.getTotal());\n        Assertions.assertEquals(2, cacheModelIPage4.getRecords().size());\n        Assertions.assertEquals(3, cache.getSize());\n        IPage<CacheModel> cacheModelIPage5 = cacheService.page(new Page<>(2, 3, true), new QueryWrapper<>());\n        Assertions.assertEquals(cacheModelIPage5.getTotal(), cacheModelIPage3.getTotal());\n        Assertions.assertEquals(2, cacheModelIPage5.getRecords().size());\n        IPage<CacheModel> cacheModelIPage6 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper<CacheModel>().ge(\"id\", 2L));\n        Assertions.assertEquals(4, cacheModelIPage6.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage6.getRecords().size());\n        IPage<CacheModel> cacheModelIPage7 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper<CacheModel>().ge(\"id\", 2L));\n        Assertions.assertEquals(0L, cacheModelIPage7.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage7.getRecords().size());\n        IPage<CacheModel> cacheModelIPage8 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper<CacheModel>().ge(\"id\", 3L));\n        Assertions.assertEquals(0L, cacheModelIPage8.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage8.getRecords().size());\n        cacheModelIPage8 = cacheService.page(new Page<>(1, 3, false), new QueryWrapper<CacheModel>().ge(\"id\", 3L));\n        Assertions.assertEquals(0L, cacheModelIPage8.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage8.getRecords().size());\n        IPage<CacheModel> cacheModelIPage9 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper<CacheModel>().ge(\"id\", 3L));\n        Assertions.assertEquals(3L, cacheModelIPage9.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage9.getRecords().size());\n        cacheModelIPage9 = cacheService.page(new Page<>(1, 3, true), new QueryWrapper<CacheModel>().ge(\"id\", 3L));\n        Assertions.assertEquals(3L, cacheModelIPage9.getTotal());\n        Assertions.assertEquals(3, cacheModelIPage9.getRecords().size());\n    }\n\n    @Test\n    @Order(2)\n    void testCleanBatchCache() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        cacheService.save(model);\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        cacheService.getById(model.getId());\n        Assertions.assertEquals(1, cache.getSize());\n        cacheService.updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        Assertions.assertEquals(0, cache.getSize());\n        Assertions.assertEquals(\"旺仔\", cacheService.getById(model.getId()).getName());\n        Assertions.assertEquals(1, cache.getSize());\n    }\n\n    @Test\n    @Order(3)\n    void testBatchTransactionalClear1() {\n        Cache cache = getCache();\n        long id = cacheService.testBatchTransactionalClear1();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertEquals(\"旺仔\", cacheModel.getName());\n    }\n\n    @Test\n    @Order(4)\n    void testBatchTransactionalClear2() {\n        long id = cacheService.testBatchTransactionalClear2();\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertEquals(\"小红\", cacheModel.getName());\n    }\n\n    @Test\n    @Order(5)\n    void testBatchTransactionalClear3() {\n        long id = cacheService.testBatchTransactionalClear3();\n        Cache cache = getCache();\n        Assertions.assertEquals(1, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertEquals(\"小红\", cacheModel.getName());\n    }\n\n    @Test\n    @Order(6)\n    void testBatchTransactionalClear4() {\n        long id = cacheService.testBatchTransactionalClear4();\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertEquals(\"旺仔\", cacheModel.getName());\n    }\n\n    @Test\n    @Order(7)\n    void testBatchTransactionalClear5() {\n        long id = cacheService.testBatchTransactionalClear5();\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertNull(cacheModel);\n    }\n\n    @Test\n    @Order(8)\n    void testBatchTransactionalClear6() {\n        long id = cacheService.testBatchTransactionalClear6();\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertNull(cacheModel);\n    }\n\n    @Test\n    @Order(9)\n    void testBatchTransactionalClear7() {\n        long id = cacheService.testBatchTransactionalClear7();\n        Cache cache = getCache();\n        Assertions.assertEquals(0, cache.getSize());\n        CacheModel cacheModel = cacheService.getById(id);\n        Assertions.assertEquals(1, cache.getSize());\n        Assertions.assertNull(cacheModel);\n    }\n\n    @Test\n    void testOrder() {\n        Cache cache = getCache();\n        cache.clear();\n        Page<CacheModel> page = new Page<>(1, 10, false);\n        page.setOrders(Collections.singletonList(OrderItem.asc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(1, cache.getSize());\n        page.setOrders(Arrays.asList(OrderItem.asc(\"id\"), OrderItem.asc(\"name\")));\n        cacheService.page(page);\n        Assertions.assertEquals(2, cache.getSize());\n        page.setOrders(Arrays.asList(OrderItem.asc(\"name\"), OrderItem.asc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(3, cache.getSize());\n        page.setOrders(Collections.singletonList(OrderItem.desc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(4, cache.getSize());\n        page = new Page<>(1, 10, true);\n        page.setOrders(Collections.singletonList(OrderItem.asc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(5, cache.getSize());\n        page.setOrders(Arrays.asList(OrderItem.asc(\"id\"), OrderItem.asc(\"name\")));\n        cacheService.page(page);\n        Assertions.assertEquals(5, cache.getSize());\n        page.setOrders(Arrays.asList(OrderItem.asc(\"name\"), OrderItem.asc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(5, cache.getSize());\n        page.setOrders(Collections.singletonList(OrderItem.desc(\"id\")));\n        cacheService.page(page);\n        Assertions.assertEquals(5, cache.getSize());\n    }\n\n    @Test\n    void testCustomOffset(){\n        Cache cache = getCache();\n        cache.clear();\n        CustomPage<CacheModel> page1 = new CustomPage<>(2, 10, false);\n        Assertions.assertEquals(0, cache.getSize());\n        cacheService.page(page1);\n        Assertions.assertEquals(1, cache.getSize());\n        cacheService.page(page1);\n        Assertions.assertEquals(1, cache.getSize());\n        //页数其他条件不变，改变分页偏移量的骚操作.\n        page1.setOffset(12L);\n        cacheService.page(page1);\n        Assertions.assertEquals(2, cache.getSize());\n        cacheService.page(page1);\n        Assertions.assertEquals(2, cache.getSize());\n    }\n\n//    @Test\n//    void testCustomSaveOrUpdateBatch(){\n//        Assertions.assertTrue(cacheService.testCustomSaveOrUpdateBatch());\n//    }\n\n    private Cache getCache() {\n        return sqlSessionFactory.getConfiguration().getCache(CacheMapper.class.getName());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CustomCache.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.cache.Cache;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Slf4j\npublic class CustomCache implements Cache {\n\n    private final String id;\n\n    private Map<Object, Object> cache = new HashMap<>();\n\n    public CustomCache(String id) {\n        this.id = id;\n    }\n\n    @Override\n    public String getId() {\n        return id;\n    }\n\n    @Override\n    public int getSize() {\n        return cache.size();\n    }\n\n    @Override\n    public void putObject(Object key, Object value) {\n        log.info(\"添加命名空间:{},缓存:{}\", id, key);\n        cache.put(key, value);\n    }\n\n    @Override\n    public Object getObject(Object key) {\n        log.info(\"获取命名空间:{},缓存:{}\", id, key);\n        return cache.get(key);\n    }\n\n    @Override\n    public Object removeObject(Object key) {\n        log.info(\"清除命名空间:{},缓存:{}\", id, key);\n        return cache.remove(key);\n    }\n\n    @Override\n    public void clear() {\n        log.info(\"清除命名空间:{}缓存\", id);\n        cache.clear();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/CustomPage.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * @author nieqiurong 2020/4/27.\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class CustomPage<T> extends Page<T> {\n    \n    private long offset;\n    \n    public CustomPage() {\n    }\n    \n    public CustomPage(long current, long size) {\n        super(current, size);\n    }\n    \n    public CustomPage(long current, long size, long total) {\n        super(current, size, total);\n    }\n    \n    public CustomPage(long current, long size, boolean isSearchCount) {\n        super(current, size, isSearchCount);\n    }\n    \n    public CustomPage(long current, long size, long total, boolean isSearchCount) {\n        super(current, size, total, isSearchCount);\n    }\n    \n    @Override\n    public long offset() {\n        return offset == 0 ? super.offset() : offset;\n    }\n    \n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/mapper/CacheMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.cache.CustomCache;\nimport com.baomidou.mybatisplus.test.h2.cache.model.CacheModel;\nimport org.apache.ibatis.annotations.CacheNamespace;\n\n@CacheNamespace(implementation = CustomCache.class)\npublic interface CacheMapper extends BaseMapper<CacheModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/model/CacheModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(value = \"t_cache\")\npublic class CacheModel implements Serializable {\n\n    private Long id;\n\n    private String name;\n\n    public CacheModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/service/ICacheService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.cache.model.CacheModel;\n\npublic interface ICacheService extends IService<CacheModel> {\n\n    long testBatchTransactionalClear1();\n\n    long testBatchTransactionalClear2();\n\n    long testBatchTransactionalClear3();\n\n    long testBatchTransactionalClear4();\n\n    long testBatchTransactionalClear5();\n\n    long testBatchTransactionalClear6();\n\n    long testBatchTransactionalClear7();\n\n//    boolean testCustomSaveOrUpdateBatch();\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/cache/service/impl/CacheServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.cache.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.cache.mapper.CacheMapper;\nimport com.baomidou.mybatisplus.test.h2.cache.model.CacheModel;\nimport com.baomidou.mybatisplus.test.h2.cache.service.ICacheService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Collections;\n\n@Service\npublic class CacheServiceImpl extends ServiceImpl<CacheMapper, CacheModel> implements ICacheService {\n\n    @Override\n    @Transactional\n    public long testBatchTransactionalClear1() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        getById(model.getId());\n        updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        return model.getId();\n    }\n\n    @Override\n    @Transactional\n    public long testBatchTransactionalClear2() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        getById(model.getId());\n        updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        model.setName(\"小红\");\n        updateById(model);\n        return model.getId();\n    }\n\n    @Override\n    @Transactional\n    public long testBatchTransactionalClear3() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        model.setName(\"小红\");\n        updateById(model);\n        getById(model.getId());\n        return model.getId();\n    }\n\n    @Override\n    public long testBatchTransactionalClear4() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        return model.getId();\n    }\n\n    @Override\n    public long testBatchTransactionalClear5() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        removeBatchByIds(Collections.singletonList(model.getId()));\n        removeById(model.getId());\n        return model.getId();\n    }\n\n    @Override\n    @Transactional\n    public long testBatchTransactionalClear6() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        removeBatchByIds(Collections.singletonList(model.getId()));\n        return model.getId();\n    }\n\n    @Override\n    @Transactional\n    public long testBatchTransactionalClear7() {\n        CacheModel model = new CacheModel(\"靓仔\");\n        save(model);\n        updateBatchById(Collections.singletonList(new CacheModel(model.getId(), \"旺仔\")));\n        removeBatchByIds(Collections.singletonList(model.getId()));\n        return model.getId();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/DBConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.config;\n\nimport org.h2.Driver;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\nimport org.springframework.jdbc.datasource.init.DataSourceInitializer;\nimport org.springframework.jdbc.datasource.init.DatabasePopulator;\nimport org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\n\n/**\n * H2 Memory Database config\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@Configuration\n@EnableTransactionManagement\npublic class DBConfig {\n\n    private String locationPattern = \"classpath:/h2/*.sql\";\n\n    @Bean\n    public DataSource dataSource(){\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public DataSourceTransactionManager transactionManager(DataSource ds) {\n        return new DataSourceTransactionManager(ds);\n    }\n\n    @Bean\n    public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws IOException {\n        final DataSourceInitializer initializer = new DataSourceInitializer();\n        initializer.setDataSource(dataSource);\n        initializer.setDatabasePopulator(databasePopulator());\n        initializer.setEnabled(true);\n        return initializer;\n    }\n\n    private DatabasePopulator databasePopulator() throws IOException {\n        ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();\n        resourceDatabasePopulator.setContinueOnError(false);\n        resourceDatabasePopulator.addScripts(\n            new PathMatchingResourcePatternResolver().getResources(locationPattern)\n        );\n        return resourceDatabasePopulator;\n    }\n\n    @Bean\n    public JdbcTemplate jdbcTemplate(DataSource ds){\n        return new JdbcTemplate(ds);\n    }\n\n    public String getLocationPattern() {\n        return locationPattern;\n    }\n\n    public void setLocationPattern(String locationPattern) {\n        this.locationPattern = locationPattern;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.config;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;\nimport com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport com.baomidou.mybatisplus.test.h2.H2MetaObjectHandler;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.transaction.PlatformTransactionManager;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport javax.sql.DataSource;\nimport java.util.List;\n\n/**\n * Mybatis Plus Config\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.mapper\")\npublic class MybatisPlusConfig {\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        sqlSessionFactory.setTypeAliasesPackage(\"com.baomidou.mybatisplus.test.h2.entity.persistent\");\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        /*\n         * 下划线转驼峰开启\n         */\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);  //默认枚举处理\n        sqlSessionFactory.setConfiguration(configuration);\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        // mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());\n        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());\n        mybatisPlusInterceptor.addInnerInterceptor(new DataChangeRecorderInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        globalConfig.setMetaObjectHandler(new H2MetaObjectHandler());\n        globalConfig.setSqlInjector(new DefaultSqlInjector() {\n\n            /**\n             * 测试注入自定义方法\n             */\n            @Override\n            public List<AbstractMethod> getMethodList(org.apache.ibatis.session.Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n                List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);\n//                methodList.add(new LogicDeleteByIdWithFill());\n                methodList.add(new AlwaysUpdateSomeColumnById(t -> t.getFieldFill() != FieldFill.INSERT));\n                methodList.add(new InsertBatchSomeColumn(t -> !(t.getFieldFill() == FieldFill.UPDATE\n                    || t.isLogicDelete() || t.getProperty().equals(\"version\"))));\n                return methodList;\n            }\n        });\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public GlobalConfig globalConfiguration() {\n        GlobalConfig conf = new GlobalConfig();\n        conf.setEnableSqlRunner(true)\n            .setDbConfig(new GlobalConfig.DbConfig()\n                .setLogicDeleteValue(\"1\")\n                .setLogicNotDeleteValue(\"0\"));\n        return conf;\n    }\n\n    @Bean\n    public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager){\n        return new TransactionTemplate(platformTransactionManager);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisPlusConfigLogicDelete.java",
    "content": "package com.baomidou.mybatisplus.test.h2.config;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;\nimport com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;\nimport com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ResourceLoader;\n\nimport javax.sql.DataSource;\nimport java.util.List;\n\n/**\n * Mybatis Plus Config\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.mapper\")\npublic class MybatisPlusConfigLogicDelete {\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, ResourceLoader resourceLoader, GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n//        sqlSessionFactory.setConfigLocation(resourceLoader.getResource(\"classpath:mybatis-config-object-factory.xml\"));\n        sqlSessionFactory.setTypeAliasesPackage(\"com.baomidou.mybatisplus.test.h2.entity.persistent\");\n        MybatisConfiguration configuration = new MybatisConfiguration();\n//        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);\n//        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        /*\n         * 下划线转驼峰开启\n         */\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);  //默认枚举处理\n        sqlSessionFactory.setConfiguration(configuration);\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        globalConfig.setSqlInjector(new DefaultSqlInjector() {\n\n            /**\n             * 测试注入自定义方法\n             */\n            @Override\n            public List<AbstractMethod> getMethodList(org.apache.ibatis.session.Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n                List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);\n                methodList.add(new LogicDeleteByIdWithFill());\n                methodList.add(new AlwaysUpdateSomeColumnById(t -> t.getFieldFill() != FieldFill.INSERT));\n                methodList.add(new InsertBatchSomeColumn(t -> !(t.getFieldFill() == FieldFill.UPDATE\n                    || t.isLogicDelete() || t.getProperty().equals(\"version\"))));\n                return methodList;\n            }\n        });\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public GlobalConfig globalConfiguration() {\n        GlobalConfig conf = new GlobalConfig();\n        conf.setEnableSqlRunner(true)\n            .setDbConfig(new GlobalConfig.DbConfig()\n                .setLogicDeleteValue(\"NOW()\")\n                .setLogicNotDeleteValue(\"NULL\")\n                .setIdType(IdType.ASSIGN_ID));\n        return conf;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/config/MybatisXmlConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.config;\n\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ClassPathResource;\n\nimport javax.sql.DataSource;\n\n/**\n * @author nieqiurong 2018/8/14 13:18.\n */\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.mapper\")\npublic class MybatisXmlConfig {\n\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        sqlSessionFactory.setTypeAliasesPackage(\"com.baomidou.mybatisplus.test.h2.entity.persistent\");\n        sqlSessionFactory.setConfigLocation(new ClassPathResource(\"mybatis-config-object-factory.xml\"));\n        return sqlSessionFactory.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/CustomFillConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.customfill;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.FieldStrategy;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport com.baomidou.mybatisplus.test.h2.customfill.annotation.InsertUpdateFill;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.apache.ibatis.type.UnknownTypeHandler;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.customfill.mapper\")\npublic class CustomFillConfig {\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        sqlSessionFactory.setConfiguration(configuration);\n        GlobalConfig globalConfig = new GlobalConfig();\n        globalConfig.setMetaObjectHandler(metaObjectHandler());\n        globalConfig.setAnnotationHandler(annotationHandler());\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public MetaObjectHandler metaObjectHandler() {\n        return new MetaObjectHandler() {\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                Object object = metaObject.getOriginalObject();\n                Class<?> clazz = object.getClass();\n\n                Field[] declaredFields = clazz.getDeclaredFields();\n                List<Field> fieldList = Arrays.stream(declaredFields)\n                    .filter(field -> metaObject.hasSetter(field.getName()))\n                    .filter(field -> {\n                        AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(TableInfoHelper.getTableInfo(clazz).getConfiguration()).getAnnotationHandler();\n                        return annotationHandler.isAnnotationPresent(field, TableField.class);\n                    })\n                    .collect(Collectors.toList());\n\n                for (Field field : fieldList) {\n                    // 此处没有做字段与列的自动转化\n                    String columnName = field.getName();\n                    setFieldValByName(columnName, \"1234567890\", metaObject);\n                }\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n\n            }\n        };\n    }\n    \n    @Bean\n    public AnnotationHandler annotationHandler() {\n        return new AnnotationHandler() {\n\n            /**\n             * 结合{@link com.baomidou.mybatisplus.test.h2.customfill.model.TestModel#getA()}简单实现{@link InsertUpdateFill} 的获取过程\n             *\n             * @param field           字段\n             * @param annotationClass 要获取的注解class\n             * @param <T>\n             * @return\n             */\n            @Override\n            public <T extends Annotation > T getAnnotation(Field field, Class<T> annotationClass) {\n\n                T annotation = field.getAnnotation(annotationClass);\n                if (annotationClass != TableField.class) {\n                    return annotation;\n                }\n\n                Annotation insertUpdateFillAnno = field.getAnnotation(InsertUpdateFill.class);\n                if (insertUpdateFillAnno == null) {\n                    return annotation;\n                }\n\n        /*\n         如果是要获取TableField场景，尝试判断是否存在InsertUpdateFill，存在则假定存在@TableField(fill = FieldFill.INSERT_UPDATE)的配置，\n         实际应用场景，应采取更加通用的方式，例如Spring的AnnotationUtils等\n         */\n                if (annotation != null) {\n                    TableField finalAnnotation = (TableField) annotation;\n                    annotation = (T) new TableField() {\n                        @Override\n                        public Class<? extends Annotation> annotationType() {\n                            return finalAnnotation.annotationType();\n                        }\n\n                        @Override\n                        public String value() {\n                            return finalAnnotation.value();\n                        }\n\n                        @Override\n                        public boolean exist() {\n                            return finalAnnotation.exist();\n                        }\n\n                        @Override\n                        public String condition() {\n                            return finalAnnotation.value();\n                        }\n\n                        @Override\n                        public String update() {\n                            return finalAnnotation.update();\n                        }\n\n                        @Override\n                        public FieldStrategy insertStrategy() {\n                            return finalAnnotation.insertStrategy();\n                        }\n\n                        @Override\n                        public FieldStrategy updateStrategy() {\n                            return finalAnnotation.updateStrategy();\n                        }\n\n                        @Override\n                        public FieldStrategy whereStrategy() {\n                            return finalAnnotation.whereStrategy();\n                        }\n\n                        @Override\n                        public FieldFill fill() {\n                            return FieldFill.INSERT_UPDATE;\n                        }\n\n                        @Override\n                        public boolean select() {\n                            return finalAnnotation.select();\n                        }\n\n                        @Override\n                        public boolean keepGlobalFormat() {\n                            return finalAnnotation.keepGlobalFormat();\n                        }\n\n                        @Override\n                        public String property() {\n                            return finalAnnotation.value();\n                        }\n\n                        @Override\n                        public JdbcType jdbcType() {\n                            return finalAnnotation.jdbcType();\n                        }\n\n                        @Override\n                        public Class<? extends TypeHandler> typeHandler() {\n                            return finalAnnotation.typeHandler();\n                        }\n\n                        @Override\n                        public boolean javaType() {\n                            return finalAnnotation.javaType();\n                        }\n\n                        @Override\n                        public String numericScale() {\n                            return finalAnnotation.value();\n                        }\n                    };\n                } else {\n                    annotation = (T) new TableField() {\n                        @Override\n                        public Class<? extends Annotation> annotationType() {\n                            return TableField.class;\n                        }\n\n                        @Override\n                        public String value() {\n                            return \"\";\n                        }\n\n                        @Override\n                        public boolean exist() {\n                            return true;\n                        }\n\n                        @Override\n                        public String condition() {\n                            return \"\";\n                        }\n\n                        @Override\n                        public String update() {\n                            return \"\";\n                        }\n\n                        @Override\n                        public FieldStrategy insertStrategy() {\n                            return FieldStrategy.DEFAULT;\n                        }\n\n                        @Override\n                        public FieldStrategy updateStrategy() {\n                            return FieldStrategy.DEFAULT;\n                        }\n\n                        @Override\n                        public FieldStrategy whereStrategy() {\n                            return FieldStrategy.DEFAULT;\n                        }\n\n                        @Override\n                        public FieldFill fill() {\n                            return FieldFill.INSERT_UPDATE;\n                        }\n\n                        @Override\n                        public boolean select() {\n                            return true;\n                        }\n\n                        @Override\n                        public boolean keepGlobalFormat() {\n                            return false;\n                        }\n\n                        @Override\n                        public String property() {\n                            return \"\";\n                        }\n\n                        @Override\n                        public JdbcType jdbcType() {\n                            return JdbcType.UNDEFINED;\n                        }\n\n                        @Override\n                        public Class<? extends TypeHandler> typeHandler() {\n                            return UnknownTypeHandler.class;\n                        }\n\n                        @Override\n                        public boolean javaType() {\n                            return false;\n                        }\n\n                        @Override\n                        public String numericScale() {\n                            return \"\";\n                        }\n                    };\n                }\n                return annotation;\n            }\n\n            @Override\n            public <T extends Annotation> boolean isAnnotationPresent(Field field, Class<T> annotationClass) {\n                return getAnnotation(field, annotationClass) != null;\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/annotation/InsertUpdateFill.java",
    "content": "package com.baomidou.mybatisplus.test.h2.customfill.annotation;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\n@TableField(fill = FieldFill.INSERT_UPDATE)\npublic @interface InsertUpdateFill {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/mapper/TestModelMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.customfill.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.customfill.model.TestModel;\n\npublic interface TestModelMapper extends BaseMapper<TestModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/customfill/model/TestModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.customfill.model;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.test.h2.customfill.annotation.InsertUpdateFill;\nimport lombok.Data;\n\n@Data\n@TableName(value = \"t_fill_test\")\npublic class TestModel {\n\n    private Long id;\n    @InsertUpdateFill\n    private String a;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String b;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2Addr.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * h2address entity.\n *\n * @author yuxiaobin\n * @since 2017/5/25\n */\n@Data\n@Accessors(chain = true)\n@TableName(\"h2address\")\npublic class H2Addr {\n\n    @TableId(\"addr_id\")\n    private Long addrId;\n\n    @TableField(\"addr_name\")\n    private String addrName;\n\n    @TableField(\"test_id\")\n    private Long testId;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2Student.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.OrderBy;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport com.baomidou.mybatisplus.test.h2.enums.GenderEnum;\nimport com.baomidou.mybatisplus.test.h2.enums.GradeEnum;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.Accessors;\n\n/**\n * 学生实体\n *\n * @author nieqiurong 2018/7/27.\n */\n@Data\n@Accessors(chain = true)\n@TableName(\"h2student\")\n@NoArgsConstructor\n@AllArgsConstructor\n@EqualsAndHashCode(callSuper = true)\npublic class H2Student extends Model<H2Student> {\n\n\n    /**\n     * serialVersionUID\n     */\n    private static final long serialVersionUID = 1290051894415073936L;\n\n    public H2Student(Long id, String name, Integer age) {\n        this.id = id;\n        this.name = name;\n        this.age = age;\n    }\n\n    @TableId(type = IdType.AUTO)\n    private Long id;\n\n    private String name;\n\n    @OrderBy\n    private GradeEnum grade;\n\n    private GenderEnum gender;\n\n    private Integer age;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2User.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDateTime;\nimport java.util.Date;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.annotation.Version;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.Accessors;\n\n/**\n * 测试用户类\n *\n * @author hubin sjy\n */\n/* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */\n@Data\n@Accessors(chain = true)\n@TableName(\"h2user\")\n@EqualsAndHashCode(callSuper = true)\npublic class H2User extends SuperEntity {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = 2043176352335589747L;\n\n\t/* 测试忽略验证 */\n    private String name;\n\n    private AgeEnum age;\n\n    /*BigDecimal 测试*/\n    private BigDecimal price;\n\n    /* 测试下划线字段命名类型, 字段填充 */\n    @TableField(fill = FieldFill.INSERT)\n    private Integer testType;\n\n    /**\n     * 转义关键字测试\n     */\n    @TableField(\"`desc`\")\n    private String desc;\n\n    /**\n     * 该注解 select 默认不注入 select 查询\n     */\n    @TableField(select = false)\n    private Date testDate;\n\n    @Version\n    private Integer version;\n\n    @TableLogic\n    private Integer deleted;\n\n    @TableField(\"created_dt\")\n    private LocalDateTime createdDt;\n\n\n    public H2User() {\n\n    }\n\n    public H2User(String name) {\n        this.name = name;\n    }\n\n    public H2User(Integer testType) {\n        this.testType = testType;\n    }\n\n    public H2User(String name, AgeEnum age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    public H2User(Long id, String name) {\n        this.setTestId(id);\n        this.name = name;\n    }\n\n    public H2User(Long id, AgeEnum age) {\n        this.setTestId(id);\n        this.age = age;\n    }\n\n    public H2User(Long id, String name, AgeEnum age, Integer testType) {\n        this.setTestId(id);\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2User(String name, AgeEnum age, Integer testType) {\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2User(String name, Integer deleted) {\n        this.name = name;\n        this.deleted = deleted;\n    }\n\n    @Override\n    public String toString() {\n        return \"h2user:{name=\" + name + \",\" +\n            \"age=\" + age + \",\" +\n            \"price=\" + price + \",\" +\n            \"testType=\" + testType + \",\" +\n            \"desc=\" + desc + \",\" +\n            \"testDate=\" + testDate + \",\" +\n            \"version=\" + version;\n    }\n\n    public static H2User ofId(Long id) {\n        H2User h2User = new H2User();\n        h2User.setTestId(id);\n        return h2User;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2UserLogicDelete.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n/**\n * 测试用户类\n *\n * @author hubin sjy\n */\n/* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */\n@Data\n@Accessors(chain = true)\n@TableName(\"h2user\")\npublic class H2UserLogicDelete {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = 2043176352335589747L;\n\n    @TableId\n    private Long testId;\n\n\t/* 测试忽略验证 */\n    private String name;\n\n    private AgeEnum age;\n\n    /*BigDecimal 测试*/\n    private BigDecimal price;\n\n    /* 测试下划线字段命名类型, 字段填充 */\n    @TableField\n    private Integer testType;\n\n    /**\n     * 转义关键字测试\n     */\n    @TableField(\"`desc`\")\n    private String desc;\n\n    /**\n     * 该注解 select 默认不注入 select 查询\n     */\n    @TableField(select = false)\n    private Date testDate;\n\n    @Version\n    private Integer version;\n\n    private Integer deleted;\n\n    @TableLogic\n    private Date lastUpdatedDt;\n\n\n    public H2UserLogicDelete() {\n\n    }\n\n    public H2UserLogicDelete(String name) {\n        this.name = name;\n    }\n\n    public H2UserLogicDelete(Integer testType) {\n        this.testType = testType;\n    }\n\n    public H2UserLogicDelete(String name, AgeEnum age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    public H2UserLogicDelete(Long id, String name) {\n        this.setTestId(id);\n        this.name = name;\n    }\n\n    public H2UserLogicDelete(Long id, AgeEnum age) {\n        this.setTestId(id);\n        this.age = age;\n    }\n\n    public H2UserLogicDelete(Long id, String name, AgeEnum age, Integer testType) {\n        this.setTestId(id);\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2UserLogicDelete(String name, AgeEnum age, Integer testType) {\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2UserLogicDelete(String name, Integer deleted) {\n        this.name = name;\n        this.deleted = deleted;\n    }\n\n    @Override\n    public String toString() {\n        return \"h2user:{name=\" + name + \",\" +\n            \"age=\" + age + \",\" +\n            \"price=\" + price + \",\" +\n            \"testType=\" + testType + \",\" +\n            \"desc=\" + desc + \",\" +\n            \"testDate=\" + testDate + \",\" +\n            \"version=\" + version;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/H2UserStrategy.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.Accessors;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\n\n/**\n * 测试用户类\n *\n * @author hubin sjy\n */\n/* 表名 value 注解【 驼峰命名可无 】, resultMap 注解测试【 映射 xml 的 resultMap 内容 】 */\n@Data\n@Accessors(chain = true)\n@TableName(\"h2user\")\n@EqualsAndHashCode(callSuper = true)\npublic class H2UserStrategy extends SuperEntity {\n\n    /**\n     * serialVersionUID\n     */\n    private static final long serialVersionUID = 2043176352335589747L;\n\n    /**\n     * whereStrategy = FieldStrategy.IGNORED 在拼接where条件时是在带上该条件\n     */\n    @TableField(value = \"name\", whereStrategy = FieldStrategy.ALWAYS)\n    private String name;\n\n    private AgeEnum age;\n\n    /*BigDecimal 测试*/\n    private BigDecimal price;\n\n    /* 测试下划线字段命名类型, 字段填充 */\n    @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.ALWAYS)\n    private Integer testType;\n\n    /**\n     * 转义关键字测试\n     *\n     * @since 2019-5-7 测试updateStrategy\n     */\n    @TableField(value = \"`desc`\", updateStrategy = FieldStrategy.ALWAYS)\n    private String desc;\n\n    /**\n     * 该注解 select 默认不注入 select 查询\n     */\n    @TableField(select = false)\n    private Date testDate;\n\n    @Version\n    private Integer version;\n\n    @TableLogic\n    private Integer deleted;\n\n\n    public H2UserStrategy() {\n\n    }\n\n    public H2UserStrategy(String name) {\n        this.name = name;\n    }\n\n    public H2UserStrategy(Integer testType) {\n        this.testType = testType;\n    }\n\n    public H2UserStrategy(String name, AgeEnum age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    public H2UserStrategy(Long id, String name) {\n        this.setTestId(id);\n        this.name = name;\n    }\n\n    public H2UserStrategy(Long id, AgeEnum age) {\n        this.setTestId(id);\n        this.age = age;\n    }\n\n    public H2UserStrategy(Long id, String name, AgeEnum age, Integer testType) {\n        this.setTestId(id);\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2UserStrategy(String name, AgeEnum age, Integer testType) {\n        this.name = name;\n        this.age = age;\n        this.testType = testType;\n    }\n\n    public H2UserStrategy(String name, Integer deleted) {\n        this.name = name;\n        this.deleted = deleted;\n    }\n\n    @Override\n    public String toString() {\n        return \"h2user:{name=\" + name + \",\" +\n            \"age=\" + age + \",\" +\n            \"price=\" + price + \",\" +\n            \"testType=\" + testType + \",\" +\n            \"desc=\" + desc + \",\" +\n            \"testDate=\" + testDate + \",\" +\n            \"version=\" + version;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuSuperEntity.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.util.Date;\n\n/**\n * 多层集成测试\n * <p>github #170</p>\n *\n * @author yuxiaobin\n * @since 2017/12/7\n */\n@Data\n@Accessors(chain = true)\npublic abstract class SuSuperEntity {\n\n    @TableField(value = \"last_updated_dt\", fill = FieldFill.UPDATE)\n    private Date lastUpdatedDt;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuSuperEntityCamel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\n\nimport java.util.Date;\n\n\n/**\n * 多层集成测试\n * <p>github #170</p>\n *\n * @author yuxiaobin\n * @since 2017/12/7\n */\npublic abstract class SuSuperEntityCamel {\n\n    @TableField(value = \"lastUpdatedDt\", fill = FieldFill.UPDATE)\n    private Date lastUpdatedDt;\n\n    public Date getLastUpdatedDt() {\n        return lastUpdatedDt;\n    }\n\n    public void setLastUpdatedDt(Date lastUpdatedDt) {\n        this.lastUpdatedDt = lastUpdatedDt;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuperEntity.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * 测试父类情况\n *\n * @author hubin\n * @since 2016-06-26\n */\n@Data\n@Accessors(chain = true)\n@EqualsAndHashCode(callSuper = true)\npublic class SuperEntity extends SuSuperEntity implements Serializable {\n\n    /**\n\t *  serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = -3111558058262086115L;\n\n\t/* 主键ID 注解，value 字段名，type 用户输入ID */\n    @TableId\n    private Long testId;\n\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/SuperEntityCamel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableId;\n\nimport java.io.Serializable;\n\n\n/**\n * 测试父类情况\n *\n * @author hubin\n * @since 2016-06-26\n */\npublic class SuperEntityCamel extends SuSuperEntityCamel implements Serializable {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = -531147777357149891L;\n\n\t/* 主键ID 注解，value 字段名，type 用户输入ID */\n    @TableId(value = \"testId\")\n    private Long id;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/AgeEnum.java",
    "content": "package com.baomidou.mybatisplus.test.h2.enums;\n\nimport com.baomidou.mybatisplus.annotation.IEnum;\n\n/**\n * 通用枚举注入演示，注意需要实现 IEnums 也需要扫描枚举包\n *\n * @author hubin\n * @since 2018-08-15\n */\npublic enum AgeEnum implements IEnum<Integer> {\n    ONE(1, \"一岁\"),\n    TWO(2, \"二岁\"),\n    THREE(3, \"三岁\");\n\n    private final int value;\n    @SuppressWarnings(\"unused\")\n    private final String desc;\n\n    AgeEnum(final int value, final String desc) {\n        this.value = value;\n        this.desc = desc;\n    }\n\n    public static AgeEnum parseValue(Integer v) {\n        if (v == null) {\n            return null;\n        }\n        for (AgeEnum e : AgeEnum.values()) {\n            if (e.getValue().equals(v)) {\n                return e;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public Integer getValue() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/GenderEnum.java",
    "content": "package com.baomidou.mybatisplus.test.h2.enums;\n\n/**\n * <p></p>\n *\n * @author yuxiaobin\n * @date 2018/8/30\n */\npublic enum GenderEnum {\n\n    MALE,\n    FEMALE\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/enums/GradeEnum.java",
    "content": "package com.baomidou.mybatisplus.test.h2.enums;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\n\n/**\n * <p></p>\n *\n * @author yuxiaobin\n * @date 2018/8/30\n */\npublic enum GradeEnum {\n\n    PRIMARY(1, \"小学\"),\n    SECONDARY(2, \"中学\"),\n    HIGH(3, \"高中\");\n\n    GradeEnum(int code, String descp) {\n        this.code = code;\n        this.descp = descp;\n    }\n\n    @EnumValue\n    private final int code;\n\n    private final String descp;\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getDescp() {\n        return descp;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/FillPerformanceConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.fillperformance;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.fillperformance.mapper\")\npublic class FillPerformanceConfig {\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        sqlSessionFactory.setConfiguration(configuration);\n        sqlSessionFactory.setGlobalConfig(new GlobalConfig().setMetaObjectHandler(metaObjectHandler()));\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public MetaObjectHandler metaObjectHandler() {\n        return new MetaObjectHandler() {\n            @Override\n            public void insertFill(MetaObject metaObject) {\n//                strictInsertFill(metaObject,\"c\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"d\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"e\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"f\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"g\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"h\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"i\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"j\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"l\",String.class,\"1234567890\");\n//                strictInsertFill(metaObject,\"m\",String.class,\"1234567890\");\n                setFieldValByName(\"c\", \"1234567890\", metaObject);\n                setFieldValByName(\"d\", \"1234567890\", metaObject);\n                setFieldValByName(\"e\", \"1234567890\", metaObject);\n                setFieldValByName(\"f\", \"1234567890\", metaObject);\n                setFieldValByName(\"g\", \"1234567890\", metaObject);\n                setFieldValByName(\"h\", \"1234567890\", metaObject);\n                setFieldValByName(\"i\", \"1234567890\", metaObject);\n                setFieldValByName(\"j\", \"1234567890\", metaObject);\n                setFieldValByName(\"l\", \"1234567890\", metaObject);\n                setFieldValByName(\"m\", \"1234567890\", metaObject);\n//                setInsertFieldValByName(\"c\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"d\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"e\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"f\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"g\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"h\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"i\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"j\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"l\",\"1234567890\",metaObject);\n//                setInsertFieldValByName(\"m\",\"1234567890\",metaObject);\n\n//                strictInsertFill(findTableInfo(metaObject),metaObject, Arrays.asList(\n//                    StrictFill.of(\"c\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"d\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"e\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"f\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"g\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"h\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"i\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"j\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"l\",String.class,\"1234567890\"),\n//                    StrictFill.of(\"m\",String.class,\"1234567890\")\n//                ));\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/mapper/PerformanceModelMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.fillperformance.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel;\n\npublic interface PerformanceModelMapper extends BaseMapper<PerformanceModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/model/PerformanceModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.fillperformance.model;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\n@TableName(value = \"t_fill_performance\")\npublic class PerformanceModel {\n\n    private Long id;\n    private String a;\n    private String b;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String c;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String d;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String e;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String f;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String g;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String h;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String i;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String j;\n    private String k;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String l;\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    private String m;\n    private String n;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/service/IPerformanceModelService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.fillperformance.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel;\n\npublic interface IPerformanceModelService extends IService<PerformanceModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/fillperformance/service/impl/PerformanceModelServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.fillperformance.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.mapper.PerformanceModelMapper;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.model.PerformanceModel;\nimport com.baomidou.mybatisplus.test.h2.fillperformance.service.IPerformanceModelService;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class PerformanceModelServiceImpl extends ServiceImpl<PerformanceModelMapper, PerformanceModel> implements IPerformanceModelService {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/CustomIdGenerator.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator;\n\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.IdWorker;\nimport org.apache.ibatis.reflection.SystemMetaObject;\n\npublic class CustomIdGenerator implements IdentifierGenerator {\n\n    @Override\n    public Number nextId(Object entity) {\n        //可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式Id调用生成.\n        String bizKey = entity.getClass().getName();\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());\n        String name = (String) SystemMetaObject.forObject(entity).getValue(\"name\");\n        //long test\n        if (tableInfo.getKeyType() == Long.class) {\n            if (\"旺仔\".equals(name)) {\n                return 666L;\n            } else if (\"靓仔\".equals(name)) {\n                return 777L;\n            }\n            return 1L;\n        } else {\n            // int test\n            if (\"旺仔\".equals(name)) {\n                return 666;\n            } else if (\"靓仔\".equals(name)) {\n                return 777;\n            }\n            return 1;\n        }\n    }\n\n    @Override\n    public String nextUUID(Object entity) {\n        String name = (String) SystemMetaObject.forObject(entity).getValue(\"name\");\n        if (\"旺仔\".equals(name)) {\n            return \"66666666666\";\n        } else if (\"靓仔\".equals(name)) {\n            return \"77777777777\";\n        }\n        return IdWorker.get32UUID();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/IdGeneratorConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.idgenerator.mapper\")\npublic class IdGeneratorConfig {\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        sqlSessionFactory.setConfiguration(configuration);\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public GlobalConfig globalConfiguration() {\n        GlobalConfig conf = new GlobalConfig();\n        //自定义Id生成器\n        conf.setIdentifierGenerator(new CustomIdGenerator());\n        return conf;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/IdentifierGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator;\n\nimport com.baomidou.mybatisplus.test.h2.idgenerator.mapper.*;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.*;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:h2/spring-id-generator-h2.xml\"})\nclass IdentifierGeneratorTest {\n\n    @Autowired\n    private LongIdGeneratorMapper longIdGeneratorMapper;\n\n    @Autowired\n    private IntegerIdGeneratorMapper integerIdGeneratorMapper;\n\n    @Autowired\n    private StringIdGeneratorMapper stringIdGeneratorMapper;\n\n    @Autowired\n    private LongStringIdGeneratorMapper longStringIdGeneratorMapper;\n\n    @Autowired\n    private IntegerStringIdGeneratorMapper integerStringIdGeneratorMapper;\n    \n    @Autowired\n    private BigDecimalIdGeneratorMapper bigDecimalIdGeneratorMapper;\n    \n    @Autowired\n    private BigIntegerIdGeneratorMapper bigIntegerIdGeneratorMapper;\n\n    @Test\n    void test() {\n        LongIdGeneratorModel longIdGeneratorModel1 = new LongIdGeneratorModel(\"旺仔\");\n        longIdGeneratorMapper.insert(longIdGeneratorModel1);\n        Assertions.assertEquals(666L, longIdGeneratorModel1.getId());\n        LongIdGeneratorModel longIdGeneratorModel2 = new LongIdGeneratorModel(\"靓仔\");\n        longIdGeneratorMapper.insert(longIdGeneratorModel2);\n        Assertions.assertEquals(777L, longIdGeneratorModel2.getId());\n\n        IntegerIdGeneratorModel integerIdGeneratorModel1 = new IntegerIdGeneratorModel(\"旺仔\");\n        integerIdGeneratorMapper.insert(integerIdGeneratorModel1);\n        Assertions.assertEquals(666, integerIdGeneratorModel1.getId());\n        IntegerIdGeneratorModel integerIdGeneratorModel2 = new IntegerIdGeneratorModel(\"靓仔\");\n        integerIdGeneratorMapper.insert(integerIdGeneratorModel2);\n        Assertions.assertEquals(777, integerIdGeneratorModel2.getId());\n\n        StringIdGeneratorModel stringIdGeneratorModel1 = new StringIdGeneratorModel(\"旺仔\");\n        stringIdGeneratorMapper.insert(stringIdGeneratorModel1);\n        Assertions.assertEquals(\"66666666666\", stringIdGeneratorModel1.getId());\n        StringIdGeneratorModel stringIdGeneratorModel2 = new StringIdGeneratorModel(\"靓仔\");\n        stringIdGeneratorMapper.insert(stringIdGeneratorModel2);\n        Assertions.assertEquals(\"77777777777\", stringIdGeneratorModel2.getId());\n\n        LongStringIdGeneratorModel longStringIdGeneratorModel1 = new LongStringIdGeneratorModel(\"旺仔\");\n        longStringIdGeneratorMapper.insert(longStringIdGeneratorModel1);\n        Assertions.assertEquals(\"666\", longStringIdGeneratorModel1.getId());\n        LongStringIdGeneratorModel longStringIdGeneratorModel2 = new LongStringIdGeneratorModel(\"靓仔\");\n        longStringIdGeneratorMapper.insert(longStringIdGeneratorModel2);\n        Assertions.assertEquals(\"777\", longStringIdGeneratorModel2.getId());\n\n\n        IntegerStringIdGeneratorModel integerStringIdGeneratorModel1 = new IntegerStringIdGeneratorModel(\"旺仔\");\n        integerStringIdGeneratorMapper.insert(integerStringIdGeneratorModel1);\n        Assertions.assertEquals(\"666\", integerStringIdGeneratorModel1.getId());\n        IntegerStringIdGeneratorModel integerStringIdGeneratorModel2 = new IntegerStringIdGeneratorModel(\"靓仔\");\n        integerStringIdGeneratorMapper.insert(integerStringIdGeneratorModel2);\n        Assertions.assertEquals(\"777\", integerStringIdGeneratorModel2.getId());\n        \n        BigDecimalIdGeneratorModel bigDecimalIdGeneratorModel = new BigDecimalIdGeneratorModel(\"测试\");\n        bigDecimalIdGeneratorMapper.insert(bigDecimalIdGeneratorModel);\n        bigDecimalIdGeneratorMapper.deleteById(bigDecimalIdGeneratorModel);\n        \n        BigIntegerIdGeneratorModel bigIntegerIdGeneratorModel = new BigIntegerIdGeneratorModel(\"测试\");\n        bigIntegerIdGeneratorMapper.insert(bigIntegerIdGeneratorModel);\n        bigIntegerIdGeneratorMapper.deleteById(bigIntegerIdGeneratorModel);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/BigDecimalIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.BigDecimalIdGeneratorModel;\n\npublic interface BigDecimalIdGeneratorMapper extends BaseMapper<BigDecimalIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/BigIntegerIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.BigIntegerIdGeneratorModel;\n\npublic interface BigIntegerIdGeneratorMapper extends BaseMapper<BigIntegerIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/IntegerIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.IntegerIdGeneratorModel;\n\npublic interface IntegerIdGeneratorMapper extends BaseMapper<IntegerIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/IntegerStringIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.IntegerStringIdGeneratorModel;\n\npublic interface IntegerStringIdGeneratorMapper extends BaseMapper<IntegerStringIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/LongIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.LongIdGeneratorModel;\n\npublic interface LongIdGeneratorMapper extends BaseMapper<LongIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/LongStringIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.LongStringIdGeneratorModel;\n\npublic interface LongStringIdGeneratorMapper extends BaseMapper<LongStringIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/mapper/StringIdGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.idgenerator.model.StringIdGeneratorModel;\n\npublic interface StringIdGeneratorMapper extends BaseMapper<StringIdGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/BigDecimalIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigDecimal;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_bigdecimal\")\npublic class BigDecimalIdGeneratorModel {\n\n    private BigDecimal id;\n\n    private String name;\n\n    public BigDecimalIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/BigIntegerIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.math.BigInteger;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_biginteger\")\npublic class BigIntegerIdGeneratorModel {\n\n    private BigInteger id;\n\n    private String name;\n\n    public BigIntegerIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/IntegerIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_int\")\npublic class IntegerIdGeneratorModel {\n\n    private Integer id;\n\n    private String name;\n\n    public IntegerIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/IntegerStringIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_int_string\")\npublic class IntegerStringIdGeneratorModel {\n\n    @TableId(type = IdType.ASSIGN_ID)\n    private String id;\n\n    private String name;\n\n    public IntegerStringIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/LongIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_long\")\npublic class LongIdGeneratorModel {\n\n    private Long id;\n\n    private String name;\n\n    public LongIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/LongStringIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_long_string\")\npublic class LongStringIdGeneratorModel {\n\n    @TableId(type = IdType.ASSIGN_ID)\n    private String id;\n\n    private String name;\n\n    public LongStringIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/idgenerator/model/StringIdGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.idgenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@TableName(value = \"t_id_generator_string\")\npublic class StringIdGeneratorModel {\n\n    @TableId(type = IdType.ASSIGN_UUID)\n    private String id;\n\n    private String name;\n\n    public StringIdGeneratorModel(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AopConfig1.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n/**\n * @author nieqiurong\n */\n@Aspect\n@EnableAspectJAutoProxy\npublic class AopConfig1 {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AopConfig1.class);\n\n    @Pointcut(\"execution(* com.baomidou.mybatisplus.test.h2.issues.aop.mapper..*.*(..))\")\n    public void point() {\n    }\n\n    @Before(\"point()\")\n    public void before() {\n        LOGGER.info(\"before ...\");\n    }\n\n    @After(\"point()\")\n    public void after() {\n        LOGGER.info(\"After ...\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AopConfig2.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author nieqiurong\n */\n@Aspect\npublic class AopConfig2 {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(AopConfig2.class);\n\n    @Pointcut(\"execution(* com.baomidou.mybatisplus.test.h2.issues.aop.mapper..*.*(..))\")\n    public void point() {\n    }\n\n    @Before(\"point()\")\n    public void before() {\n        LOGGER.info(\"before ...\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/AppConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.h2.Driver;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\n\nimport javax.sql.DataSource;\n\n/**\n * @author nieqiurong\n */\n@Configuration\n@ComponentScan(\"com.baomidou.mybatisplus.test.h2.issues.aop\")\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.issues.aop.mapper\")\npublic class AppConfig {\n\n    @Bean\n    public DataSource dataSource() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();\n        mybatisSqlSessionFactoryBean.setDataSource(dataSource);\n        mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration());\n        return mybatisSqlSessionFactoryBean.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/MultiAopTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfig.class, AopConfig1.class, AopConfig2.class})\npublic class MultiAopTest {\n\n    @Autowired\n    private IDemoService demoService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Test\n    void test() throws SQLException {\n        new SqlRunner(dataSource.getConnection()).run(\n            \"\"\"\n                 CREATE TABLE IF NOT EXISTS demo (\n                      id BIGINT NOT NULL AUTO_INCREMENT,\n                      name VARCHAR(30) NULL DEFAULT NULL ,\n                      PRIMARY KEY (id)\n                 );\n                \"\"\"\n        );\n        demoService.save(new Demo());\n        demoService.saveBatch(List.of(new Demo()));\n        demoService.getBaseMapper().insert(List.of(new Demo()));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/NoAopTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfig.class})\npublic class NoAopTest {\n\n    @Autowired\n    private IDemoService demoService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Test\n    void test() throws SQLException {\n        new SqlRunner(dataSource.getConnection()).run(\n            \"\"\"\n                 CREATE TABLE IF NOT EXISTS demo (\n                      id BIGINT NOT NULL AUTO_INCREMENT,\n                      name VARCHAR(30) NULL DEFAULT NULL ,\n                      PRIMARY KEY (id)\n                 );\n                \"\"\"\n        );\n        demoService.save(new Demo());\n        demoService.saveBatch(List.of(new Demo()));\n        demoService.getBaseMapper().insert(List.of(new Demo()));\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/SingleAopTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop;\n\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfig.class, AopConfig1.class})\npublic class SingleAopTest {\n\n    @Autowired\n    private IDemoService demoService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Test\n    void test() throws SQLException {\n        new SqlRunner(dataSource.getConnection()).run(\n            \"\"\"\n                 CREATE TABLE IF NOT EXISTS demo (\n                      id BIGINT NOT NULL AUTO_INCREMENT,\n                      name VARCHAR(30) NULL DEFAULT NULL ,\n                      PRIMARY KEY (id)\n                 );\n                \"\"\"\n        );\n        demoService.save(new Demo());\n        demoService.saveBatch(List.of(new Demo()));\n        demoService.getBaseMapper().insert(List.of(new Demo()));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/entity/Demo.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop.entity;\n\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\npublic class Demo {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/mapper/DemoMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author nieqiurong\n */\n@Mapper\npublic interface DemoMapper extends BaseMapper<Demo> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/service/IDemoService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\n\n/**\n * @author nieqiurong\n */\npublic interface IDemoService extends IService<Demo> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/aop/service/impl/DemoServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.aop.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.mapper.DemoMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.aop.service.IDemoService;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author nieqiurong\n */\n@Service\npublic class DemoServiceImpl extends ServiceImpl<DemoMapper, Demo> implements IDemoService {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/GenericIdTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid;\n\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.entity.LongEntity;\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.entity.StringEntity;\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.mapper.LongEntityMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.mapper.StringEntityMapper;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\n/**\n * 泛型主键问题\n * https://gitee.com/baomidou/mybatis-plus/issues/I171CQ\n *\n * @author nieqiuqiu\n * @date 2019-12-20\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:issues/genericid/spring.xml\"})\nclass GenericIdTest {\n\n    @Autowired\n    private LongEntityMapper longEntityMapper;\n\n    @Autowired\n    private StringEntityMapper stringEntityMapper;\n\n    @Test\n    void test() {\n        longEntityMapper.insert(new LongEntity(\"testLong\"));\n        stringEntityMapper.insert(new StringEntity(\"testString\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/MybatisPlusConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\n\n/**\n * config\n *\n * @author nieqiuqiu\n * @date 2019-12-20\n */\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.issues.genericid.mapper\")\npublic class MybatisPlusConfig {\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        sqlSessionFactory.setConfiguration(configuration);\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n        return sqlSessionFactory.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/LongEntity.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(\"t_i171cq_long\")\n@EqualsAndHashCode(callSuper = true)\npublic class LongEntity extends SuperEntity<Long> {\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/StringEntity.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(\"t_i171cq_string\")\n@EqualsAndHashCode(callSuper = true)\npublic class StringEntity extends SuperEntity<String> {\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/entity/SuperEntity.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n@Data\nclass SuperEntity<ID extends Serializable> {\n\n    @TableId(type = IdType.ASSIGN_ID)\n    private ID id;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/mapper/LongEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.entity.LongEntity;\n\n/**\n * long mapper\n *\n * @author nieqiuqiu\n * @date 2019-12-20\n */\npublic interface LongEntityMapper extends BaseMapper<LongEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/genericid/mapper/StringEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.genericid.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.genericid.entity.StringEntity;\n\n/**\n * string mapper\n *\n * @author nieqiuqiu\n * @date 2019-12-20\n */\npublic interface StringEntityMapper extends BaseMapper<StringEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/AppConfig.java",
    "content": "\npackage com.baomidou.mybatisplus.test.h2.issues.repositoryscan;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.h2.Driver;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\n\nimport javax.sql.DataSource;\n\n/**\n * @author nieqiurong\n */\n@Configuration\n@ComponentScan(\"com.baomidou.mybatisplus.test.h2.issues.repositoryscan\")\npublic class AppConfig {\n\n    @Bean\n    public DataSource dataSource() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();\n        mybatisSqlSessionFactoryBean.setDataSource(dataSource);\n        mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration());\n        return mybatisSqlSessionFactoryBean.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/AppConfigWithMapperScan.java",
    "content": "\npackage com.baomidou.mybatisplus.test.h2.issues.repositoryscan;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.h2.Driver;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\nimport org.springframework.stereotype.Repository;\n\nimport javax.sql.DataSource;\n\n/**\n * @author nieqiurong\n */\n@Configuration\n@ComponentScan(\"com.baomidou.mybatisplus.test.h2.issues.repositoryscan\")\n@MapperScan(value = \"com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper\", annotationClass = Repository.class)\npublic class AppConfigWithMapperScan {\n\n    @Bean\n    public DataSource dataSource() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();\n        mybatisSqlSessionFactoryBean.setDataSource(dataSource);\n        mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration());\n        return mybatisSqlSessionFactoryBean.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/RepositoryDefaultScanTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan;\n\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfig.class})\npublic class RepositoryDefaultScanTest {\n\n    @Autowired\n    private IDemoRepositoryService demoService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Test\n    void test() throws SQLException {\n        new SqlRunner(dataSource.getConnection()).run(\n            \"\"\"\n                 CREATE TABLE IF NOT EXISTS demo (\n                      id BIGINT NOT NULL AUTO_INCREMENT,\n                      name VARCHAR(30) NULL DEFAULT NULL ,\n                      PRIMARY KEY (id)\n                 );\n                \"\"\"\n        );\n        demoService.save(new Demo());\n        demoService.saveBatch(List.of(new Demo()));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/RepositoryMapperScanTest.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan;\n\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfigWithMapperScan.class})\npublic class RepositoryMapperScanTest {\n\n    @Autowired\n    private IDemoRepositoryService demoService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Test\n    void test() throws SQLException {\n        new SqlRunner(dataSource.getConnection()).run(\n            \"\"\"\n                 CREATE TABLE IF NOT EXISTS demo (\n                      id BIGINT NOT NULL AUTO_INCREMENT,\n                      name VARCHAR(30) NULL DEFAULT NULL ,\n                      PRIMARY KEY (id)\n                 );\n                \"\"\"\n        );\n        demoService.save(new Demo());\n        demoService.saveBatch(List.of(new Demo()));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/entity/Demo.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity;\n\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\npublic class Demo {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/mapper/DemoRepositoryMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo;\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author nieqiurong\n */\n@Repository\npublic interface DemoRepositoryMapper extends BaseMapper<Demo> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/service/IDemoRepositoryService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo;\n\n/**\n * @author nieqiurong\n */\npublic interface IDemoRepositoryService extends IService<Demo> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/issues/repositoryscan/service/impl/DemoRepositoryServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.entity.Demo;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.mapper.DemoRepositoryMapper;\nimport com.baomidou.mybatisplus.test.h2.issues.repositoryscan.service.IDemoRepositoryService;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author nieqiurong\n */\n@Service\npublic class DemoRepositoryServiceImpl extends ServiceImpl<DemoRepositoryMapper, Demo> implements IDemoRepositoryService {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/KeyGeneratorConfig.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\nimport java.util.Arrays;\n\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.keygenerator.mapper\")\npublic class KeyGeneratorConfig {\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        sqlSessionFactory.setDataSource(dataSource);\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        configuration.setMapUnderscoreToCamelCase(true);\n        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n        sqlSessionFactory.setConfiguration(configuration);\n\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public GlobalConfig globalConfiguration() {\n        GlobalConfig conf = new GlobalConfig();\n        conf.setDbConfig(new GlobalConfig.DbConfig().setKeyGenerators(Arrays.asList(\n            new H2KeyGenerator(),\n            new IKeyGenerator() {\n\n                @Override\n                public String executeSql(String incrementerName) {\n                    return \"select nextval('\" + incrementerName + \"')\";\n                }\n\n                @Override\n                public DbType dbType() {\n                    return DbType.POSTGRE_SQL;\n                }\n            }\n        )));\n        return conf;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/ExtendKeyGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.ExtendKeyGeneratorModel;\n\npublic interface ExtendKeyGeneratorMapper extends BaseMapper<ExtendKeyGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/IntegerKeyGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.IntegerKeyGeneratorModel;\n\npublic interface IntegerKeyGeneratorMapper extends BaseMapper<IntegerKeyGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/KeyGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.KeyGeneratorModel;\n\npublic interface KeyGeneratorMapper extends BaseMapper<KeyGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/LongKeyGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.LongKeyGeneratorModel;\n\npublic interface LongKeyGeneratorMapper extends BaseMapper<LongKeyGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/mapper/StringKeyGeneratorMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.keygenerator.model.StringKeyGeneratorModel;\n\npublic interface StringKeyGeneratorMapper extends BaseMapper<StringKeyGeneratorModel> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/BaseMode.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.KeySequence;\n\n@KeySequence(value = \"key_generator_model_seq\")\nclass BaseMode {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/ExtendKeyGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n@Data\n@TableName(value = \"key_generator_model\")\n@EqualsAndHashCode(callSuper = true)\npublic class ExtendKeyGeneratorModel extends BaseMode {\n\n    @TableId(type = IdType.INPUT, value = \"id\")\n    private Long uid;\n\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/IntegerKeyGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n@Data\n@TableName(value = \"key_generator_model\")\n@KeySequence(value = \"key_generator_model_seq\")\npublic class IntegerKeyGeneratorModel {\n\n    @TableId(type = IdType.INPUT, value = \"id\")\n    private Integer uid;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/KeyGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n@Data\n@TableName(value = \"key_generator_model\")\n@KeySequence(value = \"key_generator_model_seq\")\npublic class KeyGeneratorModel {\n\n    @TableId(type = IdType.INPUT, value = \"id\")\n    private Long uid;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/LongKeyGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\n\n@Data\n@TableName(value = \"key_generator_model\")\n@KeySequence(value = \"key_generator_model_seq\", dbType = DbType.POSTGRE_SQL)\npublic class LongKeyGeneratorModel {\n\n    @TableId(type = IdType.INPUT)\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/keygenerator/model/StringKeyGeneratorModel.java",
    "content": "package com.baomidou.mybatisplus.test.h2.keygenerator.model;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n@Data\n@TableName(value = \"key_generator_model\")\n@KeySequence(value = \"key_generator_model_seq\")\npublic class StringKeyGeneratorModel {\n\n    @TableId(type = IdType.INPUT)\n    private String id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2StudentMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.mapper;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 学生Mapper层\n * @author nieqiurong 2018/7/27.\n */\npublic interface H2StudentMapper extends SuperMapper<H2Student> {\n\n    long insertFillByCustomMethod1(H2User h2User);\n\n    long insertFillByCustomMethod2(@Param(\"et\") H2User h2User);\n\n    long insertFillByCustomMethod3(@Param(\"et\") H2User h2User, @Param(\"test\") String test);\n\n    long insertFillByCustomMethod4(Collection<H2User> h2User);\n\n    long insertFillByCustomMethod5(@Param(\"collection\") Collection<H2User> h2User);\n\n    long insertFillByCustomMethod6(@Param(\"coll\") Collection<H2User> h2User);\n\n    long insertFillByCustomMethod7(@Param(\"list\") List<H2User> h2User);\n\n    long insertFillByCustomMethod8(H2User[] h2Users);\n\n    long insertFillByCustomMethod9(@Param(\"array\") H2User[] h2Users);\n\n    long insertFillByCustomMethod10(Map<String, Object> paramMap);\n\n    long insertFillByCustomMethod11(Map<String, Object> paramMap);\n\n    long insertFillByCustomMethod12(Map<String, Object> paramMap);\n\n    long insertFillByCustomMethod13(Map<String, Object> paramMap);\n\n    long updateFillByCustomMethod1(Map<String, Object> paramMap);\n\n    long updateFillByCustomMethod2(@Param(\"coll\") Collection<Long> ids, @Param(\"et\") H2User h2User);\n\n    long updateFillByCustomMethod3(@Param(\"coll\") Collection<Long> ids, @Param(\"user\") H2User h2User);\n\n    long updateFillByCustomMethod4(@Param(\"colls\") Collection<Long> ids, @Param(\"et\") H2User h2User);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserLogicDeleteMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.mapper;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2UserLogicDelete;\n\n/**\n * 这里继承自定义父类 SuperMapper\n *\n * @author Caratacus\n * @since 2017/4/1\n */\npublic interface H2UserLogicDeleteMapper extends SuperMapper<H2UserLogicDelete> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.mapper;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Options;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.apache.ibatis.mapping.StatementType;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Addr;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\n\n/**\n * 这里继承自定义父类 SuperMapper\n *\n * @author Caratacus\n * @since 2017/4/1\n */\npublic interface H2UserMapper extends SuperMapper<H2User> {\n\n    @Select(\n        \"select a.addr_id as addrId, a.addr_name as addrName from h2address a\" +\n            \" join h2user u on u.test_id=a.test_id and u.test_id=#{userId}\"\n    )\n    List<H2Addr> getAddrListByUserId(@Param(\"userId\") Long userId);\n\n    @Select(\n        \"select a.addr_id as addrId, a.addr_name as addrName from h2address a\" +\n            \" join h2user u on u.test_id=a.test_id and u.test_id=#{userId}\"\n    )\n    List<H2Addr> getAddrListByUserIdPage(@Param(\"userId\") Long userId, IPage<H2Addr> page);\n\n    @Insert(\n        \"insert into h2user(name,version) values(#{name},#{version})\"\n    )\n    int myInsertWithNameVersion(@Param(\"name\") String name, @Param(\"version\") int version);\n\n    @Update(\n        \"update h2user set version=version+1, name=#{name} where test_id=#{id} and test_type=1\"\n    )\n    int myUpdateWithNameId(@Param(\"id\") Long id, @Param(\"name\") String name);\n\n\n    @Insert(\n        \"insert into h2user(name,version) values( #{user1.name}, #{user1.version})\"\n    )\n    int myInsertWithParam(@Param(\"user1\") H2User user1);\n\n    @Insert(\n        \"insert into h2user(name,version) values( #{name}, #{version})\"\n    )\n    int myInsertWithoutParam(H2User user1);\n\n    @Select(\" select test_id as testId, power(#{ageFrom},2), 'abc?zhazha', CAST(#{nameParam} AS VARCHAR) as name \" +\n        \" from h2user \" +\n        \" where age>#{ageFrom} and age<#{ageTo} \")\n    List<H2User> selectUserWithParamInSelectStatememt(Map<String, Object> param);\n\n    @Select(\"select * from h2user ${ew.customSqlSegment}\")\n    List<H2User> selectTestCustomSqlSegment(@Param(Constants.WRAPPER) Wrapper wrapper);\n\n    @Select(\"select count(1) from (\" +\n        \"select test_id as id, CAST(#{nameParam} AS VARCHAR) as name\" +\n        \" from h2user \" +\n        \" where age>#{ageFrom} and age<#{ageTo} \" +\n        \") a\")\n    int selectCountWithParamInSelectItems(Map<String, Object> param);\n\n    @Select(\"select age,name,count(age) from h2user group by age,name order by age\")\n    List<Map<?, ?>> mySelectMaps(IPage<H2User> page);\n\n    @Select(\"call 1\")\n    @Options(statementType = StatementType.CALLABLE)\n    String testCall();\n\n    @Select(\"select * from h2user\")\n    IPage<H2User> testPage1(@Param(value = \"user\") H2User h2User, @Param(value = \"page\") Page page);\n\n    @Select(\"select * from h2user\")\n    IPage<H2User> testPage2(@Param(value = \"user\") Page page, @Param(value = \"page\") H2User h2User);\n\n    @Select(\"select count(*) from h2user\")\n    Long selectCountLong();\n\n    @Select(\"select * from h2user where test_id = #{id}\")\n    H2User getById(@Param(\"id\") Long id);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserStrategyMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.mapper;\n\nimport com.baomidou.mybatisplus.test.h2.entity.H2UserStrategy;\n\n/**\n * 这里继承自定义父类 SuperMapper\n *\n * @author Caratacus\n * @since 2017/4/1\n */\npublic interface H2UserStrategyMapper extends SuperMapper<H2UserStrategy> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/SuperMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.mapper;\n\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 自定义父类 SuperMapper\n *\n * @author hubin\n * @since 2017-06-26\n */\npublic interface SuperMapper<T> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T> {\n\n    /**\n     * 这里注入自定义的公共方法\n     */\n\n    int alwaysUpdateSomeColumnById(@Param(\"et\") T entity);\n\n    int deleteByIdWithFill(T entity);\n\n    int insertBatchSomeColumn(List<T> entityList);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/IH2StudentService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\n\n/**\n * Created by nieqiuqiu on 2018/9/6.\n */\npublic interface IH2StudentService extends IService<H2Student> {\n\n    void testTransactional();\n\n    void testSqlRunnerTransactional();\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/IH2UserService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.service;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Service层测试\n *\n * @author hubin\n * @since 2017-01-30\n */\npublic interface IH2UserService extends IService<H2User> {\n\n    int myInsert(String name, int version);\n\n    int myInsertWithParam(String name, int version);\n\n    int myInsertWithoutParam(String name, int version);\n\n    int myUpdate(Long id, String name);\n\n    List<H2User> queryWithParamInSelectStatememt(Map<String, Object> param);\n\n    IPage<H2User> queryWithParamInSelectStatememt4Page(Map<String, Object> param, IPage<H2User> page);\n\n    int selectCountWithParamInSelectItems(Map<String, Object> param);\n\n    List<Map<?, ?>> mySelectMaps();\n\n    void testBatchTransactional();\n\n    void testSimpleTransactional();\n\n    void testSaveOrUpdateBatchTransactional();\n\n    void testSimpleAndBatchTransactional();\n\n    void testSaveBatchNoTransactional1();\n\n    void testSaveBatchNoTransactional2();\n\n    List<H2User> testCustomSqlSegment(Wrapper wrapper);\n\n    void testSaveOrUpdateTransactional1(List<H2User> users);\n\n    void testSaveOrUpdateTransactional2(List<H2User> users);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/impl/H2StudentServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.service.impl;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner;\nimport com.baomidou.mybatisplus.test.h2.entity.H2Student;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper;\nimport com.baomidou.mybatisplus.test.h2.service.IH2StudentService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n/**\n * Created by Administrator on 2018/9/6.\n */\n@Service\npublic class H2StudentServiceImpl extends ServiceImpl<H2StudentMapper,H2Student> implements IH2StudentService {\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testTransactional() {\n        new H2Student(null, \"tx1\", 2).insert();\n        new H2Student(null, \"tx2\", 2).insert();\n        new H2Student(null, \"tx3\", 2).insert();\n        throw new MybatisPlusException(\"测试AR事务回滚\");\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSqlRunnerTransactional() {\n        SqlRunner.db().insert(\"INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )\",\"sqlRunnerTx1\",2);\n        SqlRunner.db().insert(\"INSERT INTO h2student ( name, age ) VALUES ( {0}, {1} )\",\"sqlRunnerTx2\",2);\n        throw new MybatisPlusException(\"测试普通插入事务回滚\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/impl/H2UserServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.service.impl;\n\nimport com.baomidou.mybatisplus.core.batch.MybatisBatch;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisBatchUtils;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.entity.H2User;\nimport com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;\nimport com.baomidou.mybatisplus.test.h2.service.IH2UserService;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Service层测试\n *\n * @author hubin\n * @since 2017-01-30\n */\n@Service\npublic class H2UserServiceImpl extends ServiceImpl<H2UserMapper, H2User> implements IH2UserService {\n\n    @Override\n    public int myInsert(String name, int version) {\n        return baseMapper.myInsertWithNameVersion(name, version);\n    }\n\n    @Override\n    public int myInsertWithParam(String name, int version) {\n        H2User user = new H2User();\n        user.setName(name);\n        user.setVersion(version);\n        return baseMapper.myInsertWithParam(user);\n    }\n\n    @Override\n    public int myInsertWithoutParam(String name, int version) {\n        H2User user = new H2User();\n        user.setName(name);\n        user.setVersion(version);\n        return baseMapper.myInsertWithoutParam(user);\n    }\n\n    @Override\n    public int myUpdate(Long id, String name) {\n        return baseMapper.myUpdateWithNameId(id, name);\n    }\n\n    @Override\n    public List<H2User> queryWithParamInSelectStatememt(Map<String, Object> param) {\n        return baseMapper.selectUserWithParamInSelectStatememt(param);\n    }\n\n    @Override\n    public IPage<H2User> queryWithParamInSelectStatememt4Page(Map<String, Object> param, IPage<H2User> page) {\n        //page.setSearchCount(true);\n//        userMapper.selectUserWithParamInSelectStatememt4Page(param, page);\n        return page;\n    }\n\n    @Override\n    public int selectCountWithParamInSelectItems(Map<String, Object> param) {\n        return baseMapper.selectCountWithParamInSelectItems(param);\n    }\n\n    @Override\n    public List<Map<?,?>> mySelectMaps() {\n        Page<H2User> page = new Page<>(1,3);\n        page.addOrder(OrderItem.asc(\"name\"));\n        return baseMapper.mySelectMaps(page);\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testBatchTransactional() {\n        saveBatch(Arrays.asList(new H2User(\"batch1\", 0), new H2User(\"batch2\", 0), new H2User(\"batch3\", 0)));\n        saveBatch(Arrays.asList(new H2User(\"batch4\", 0), new H2User(\"batch5\", 0), new H2User(\"batch6\", 0)));\n        throw new MybatisPlusException(\"测试批量插入事务回滚\");\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSimpleTransactional() {\n        save(new H2User(\"simple1\", 0));\n        save(new H2User(\"simple2\", 0));\n        throw new MybatisPlusException(\"测试普通插入事务回滚\");\n    }\n\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSaveOrUpdateBatchTransactional() {\n        saveOrUpdateBatch(Arrays.asList(new H2User(\"savOrUpdate1\", 0), new H2User(\"savOrUpdate2\", 0), new H2User(\"savOrUpdate3\", 0)), 1);\n        saveOrUpdateBatch(Arrays.asList(new H2User(\"savOrUpdate4\", 0), new H2User(\"savOrUpdate5\", 0), new H2User(\"savOrUpdate6\", 0)), 1);\n        throw new MybatisPlusException(\"测试普通插入事务回滚\");\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSimpleAndBatchTransactional() {\n        save(new H2User(\"simpleAndBatchTx1\", 0));\n        saveBatch(Arrays.asList(new H2User(\"simpleAndBatchTx2\", 0), new H2User(\"simpleAndBatchTx3\", 0), new H2User(\"simpleAndBatchTx4\", 0)), 1);\n        saveOrUpdateBatch(Arrays.asList(new H2User(\"simpleAndBatchTx5\", 0), new H2User(\"simpleAndBatchTx6\", 0), new H2User(\"simpleAndBatchTx7\", 0)), 1);\n        throw new MybatisPlusException(\"测试事务回滚\");\n    }\n\n    @Override\n    public void testSaveBatchNoTransactional1() {\n        saveBatch(Arrays.asList(new H2User(\"testSaveBatchNoTransactional1\", 0), new H2User(\"testSaveBatchNoTransactional1\", 0), new H2User(\"testSaveBatchNoTransactional1\", 0)), 1);\n    }\n\n    @Override\n    public void testSaveBatchNoTransactional2() {\n        //非事物下，制造一个批量主键冲突\n        save(new H2User(1577431655447L, \"testSaveBatchNoTransactional2\"));\n        saveBatch(Arrays.asList(new H2User(\"testSaveBatchNoTransactional2\", 0), new H2User(\"testSaveBatchNoTransactional2\", 0), new H2User(1577431655447L, \"testSaveBatchNoTransactional2\")), 1);\n    }\n\n    @Override\n    public List<H2User> testCustomSqlSegment(Wrapper wrapper) {\n        return baseMapper.selectTestCustomSqlSegment(wrapper);\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSaveOrUpdateTransactional1(List<H2User> users) {\n        var method = new MybatisBatch.Method<H2User>(H2UserMapper.class);\n        MybatisBatchUtils.saveOrUpdate(getSqlSessionFactory(), users, method.insert(), (sqlSession, user) -> this.getById(user.getTestId()) == null, method.updateById());\n    }\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class)\n    public void testSaveOrUpdateTransactional2(List<H2User> users) {\n        var method = new MybatisBatch.Method<H2User>(H2UserMapper.class);\n        MybatisBatchUtils.saveOrUpdate(getSqlSessionFactory(), users, method.insert(), (sqlSession, user) -> sqlSession.selectList(method.get(\"selectById\").getStatementId(), user.getTestId()).isEmpty(), method.updateById());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/CustomCacheExecutor.java",
    "content": "//package com.baomidou.mybatisplus.test.h2.tenant;\n//\n//import com.baomidou.mybatisplus.core.executor.MybatisCachingExecutor;\n//import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;\n//import org.apache.ibatis.cache.CacheKey;\n//import org.apache.ibatis.executor.Executor;\n//import org.apache.ibatis.mapping.BoundSql;\n//import org.apache.ibatis.mapping.MappedStatement;\n//import org.apache.ibatis.mapping.SqlCommandType;\n//import org.apache.ibatis.session.RowBounds;\n//\n//import java.util.Objects;\n//\n//\n///**\n// * @author nieqiuqiu 2020/6/15\n// */\n//public class CustomCacheExecutor extends MybatisCachingExecutor {\n//\n//    private TenantHandler tenantHandler;\n//\n//    public CustomCacheExecutor(Executor delegate, TenantHandler tenantHandler) {\n//        super(delegate);\n//        this.tenantHandler = tenantHandler;\n//    }\n//\n//    @Override\n//    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {\n//        if (ms.getSqlCommandType() == SqlCommandType.SELECT) {\n//            CacheKey cacheKey = super.createCacheKey(ms, parameterObject, rowBounds, boundSql);\n//            cacheKey.update(Objects.toString(tenantHandler.getTenantId(true)));\n//            return cacheKey;\n//        }\n//        return super.createCacheKey(ms, parameterObject, rowBounds, boundSql);\n//    }\n//\n//    @Override\n//    protected CacheKey getCountCacheKey(MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject, RowBounds rowBounds) {\n//        CacheKey countCacheKey = super.getCountCacheKey(mappedStatement, boundSql, parameterObject, rowBounds);\n//        countCacheKey.update(Objects.toString(tenantHandler.getTenantId(true)));\n//        return countCacheKey;\n//    }\n//\n//}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/TenantConfig.java",
    "content": "//package com.baomidou.mybatisplus.test.h2.tenant;\n//\n//import com.baomidou.mybatisplus.core.MybatisConfiguration;\n//import com.baomidou.mybatisplus.core.executor.MybatisBatchExecutor;\n//import com.baomidou.mybatisplus.core.executor.MybatisReuseExecutor;\n//import com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor;\n//import com.baomidou.mybatisplus.core.parser.ISqlParser;\n//import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;\n//import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;\n//import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;\n//import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\n//import net.sf.jsqlparser.expression.Expression;\n//import net.sf.jsqlparser.expression.LongValue;\n//import org.apache.ibatis.executor.Executor;\n//import org.apache.ibatis.session.ExecutorType;\n//import org.apache.ibatis.session.SqlSessionFactory;\n//import org.apache.ibatis.transaction.Transaction;\n//import org.apache.ibatis.type.EnumOrdinalTypeHandler;\n//import org.apache.ibatis.type.JdbcType;\n//import org.mybatis.spring.annotation.MapperScan;\n//import org.springframework.context.annotation.Bean;\n//import org.springframework.context.annotation.Configuration;\n//\n//import javax.sql.DataSource;\n//import java.util.ArrayList;\n//import java.util.List;\n//\n///**\n// * @author nieqiuqiu 2019/12/8\n// */\n//@Configuration\n//@MapperScan(\"com.baomidou.mybatisplus.test.h2.tenant.mapper\")\n//public class TenantConfig {\n//\n//    //模拟用户切换\n//    static Long TENANT_ID = 1L;\n//\n//    @Bean\n//    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n//        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n//        sqlSessionFactory.setDataSource(dataSource);\n//        TenantSqlParser tenantSqlParser = new TenantSqlParser();\n//        tenantSqlParser.setTenantHandler(new TenantHandler() {\n//            @Override\n//            public Expression getTenantId(boolean select) {\n//                return new LongValue(TENANT_ID);\n//            }\n//\n//            @Override\n//            public String getTenantIdColumn() {\n//                return \"tenant_id\";\n//            }\n//\n//            @Override\n//            public boolean doTableFilter(String tableName) {\n//                return false;\n//            }\n//        });\n//        MybatisConfiguration configuration = new MybatisConfiguration() {\n//            @Override\n//            public Executor newExecutor(Transaction transaction, ExecutorType executorType) {\n//                executorType = executorType == null ? defaultExecutorType : executorType;\n//                executorType = executorType == null ? ExecutorType.SIMPLE : executorType;\n//                Executor executor;\n//                if (ExecutorType.BATCH == executorType) {\n//                    executor = new MybatisBatchExecutor(this, transaction);\n//                } else if (ExecutorType.REUSE == executorType) {\n//                    executor = new MybatisReuseExecutor(this, transaction);\n//                } else {\n//                    executor = new MybatisSimpleExecutor(this, transaction);\n//                }\n//                if (cacheEnabled) {\n//                    executor = new CustomCacheExecutor(executor, tenantSqlParser.getTenantHandler());\n//                }\n//                executor = (Executor) interceptorChain.pluginAll(executor);\n//                return executor;\n//            }\n//        };\n//        configuration.setJdbcTypeForNull(JdbcType.NULL);\n//        configuration.setMapUnderscoreToCamelCase(true);\n//        configuration.setDefaultExecutorType(ExecutorType.REUSE);\n//        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler.class);\n//        configuration.setCacheEnabled(true);\n//        sqlSessionFactory.setConfiguration(configuration);\n//        PaginationInterceptor pagination = new PaginationInterceptor();\n//        List<ISqlParser> sqlParserList = new ArrayList<>();\n//        sqlParserList.add(tenantSqlParser);\n//        pagination.setSqlParserList(sqlParserList);\n//        sqlSessionFactory.setPlugins(pagination);\n//        return sqlSessionFactory.getObject();\n//    }\n//\n//}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/TenantTest.java",
    "content": "//package com.baomidou.mybatisplus.test.h2.tenant;\n//\n//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\n//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;\n//import com.baomidou.mybatisplus.test.h2.tenant.model.Student;\n//import com.baomidou.mybatisplus.test.h2.tenant.service.IStudentService;\n//import org.junit.jupiter.api.Assertions;\n//import org.junit.jupiter.api.MethodOrderer;\n//import org.junit.jupiter.api.Test;\n//import org.junit.jupiter.api.TestMethodOrder;\n//import org.junit.jupiter.api.extension.ExtendWith;\n//import org.springframework.beans.factory.annotation.Autowired;\n//import org.springframework.test.context.ContextConfiguration;\n//import org.springframework.test.context.junit.jupiter.SpringExtension;\n//\n//import java.util.List;\n//\n///**\n// * @author nieqiuqiu 2019/12/8\n// */\n//@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n//@ExtendWith(SpringExtension.class)\n//@ContextConfiguration(locations = {\"classpath:h2/spring-tenant-h2.xml\"})\n//class TenantTest {\n//\n//    @Autowired\n//    private IStudentService studentService;\n//\n//    @Test\n//    void testSimple() {\n//        TenantConfig.TENANT_ID = 2L;\n//        Student student1 = studentService.getById(1L);\n//        Assertions.assertNull(student1);\n//        student1 = studentService.getById(1L);\n//        Assertions.assertNull(student1);\n//        TenantConfig.TENANT_ID = 1L;\n//        Student student2 = studentService.getById(1L);\n//        Assertions.assertNotNull(student2);\n//        student2 = studentService.getById(1L);\n//        Assertions.assertNotNull(student2);\n//    }\n//\n//    @Test\n//    void testPage() {\n//        TenantConfig.TENANT_ID = 2L;\n//        Page<Student> page1 = studentService.page(new Page<>(0, 10), new QueryWrapper<>());\n//        Assertions.assertEquals(page1.getTotal(), 1);\n//        page1 = studentService.page(new Page<>(0, 10), new QueryWrapper<>());\n//        Assertions.assertEquals(page1.getTotal(), 1);\n//        TenantConfig.TENANT_ID = 3L;\n//        Page<Student> page2 = studentService.page(new Page<>(0, 10), new QueryWrapper<>());\n//        Assertions.assertEquals(page2.getTotal(), 0);\n//        page2 = studentService.page(new Page<>(0, 10), new QueryWrapper<>());\n//        Assertions.assertEquals(page2.getTotal(), 0);\n//    }\n//\n//    @Test\n//    void testBatch() {\n//        TenantConfig.TENANT_ID = 1L;\n//        List<Student> list = studentService.list();\n//        list.forEach(student -> student.setName(\"小红\"));\n//        studentService.updateBatchById(list);\n//        studentService.list().forEach(student -> Assertions.assertEquals(\"小红\", student.getName()));\n//    }\n//}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/mapper/StudentMapper.java",
    "content": "package com.baomidou.mybatisplus.test.h2.tenant.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.h2.tenant.model.Student;\nimport org.apache.ibatis.annotations.CacheNamespace;\n\n/**\n * @author nieqiuqiu 2019/12/8\n */\n@CacheNamespace\npublic interface StudentMapper extends BaseMapper<Student> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/model/Student.java",
    "content": "package com.baomidou.mybatisplus.test.h2.tenant.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * @author nieqiuqiu 2019/12/8\n */\n@Data\n@TableName(value = \"student\")\n@NoArgsConstructor\npublic class Student implements Serializable {\n\n    private Long id;\n\n    private String tenantId;\n\n    private String name;\n\n    public Student(String name) {\n        this.name = name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/service/IStudentService.java",
    "content": "package com.baomidou.mybatisplus.test.h2.tenant.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.h2.tenant.model.Student;\n\npublic interface IStudentService extends IService<Student> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/tenant/service/impl/StudentServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.h2.tenant.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.h2.tenant.mapper.StudentMapper;\nimport com.baomidou.mybatisplus.test.h2.tenant.model.Student;\nimport com.baomidou.mybatisplus.test.h2.tenant.service.IStudentService;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2Entity.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(value = \"t_fastjson2_entity\", autoResultMap = true)\npublic class FastJson2Entity {\n\n    @TableId(type = IdType.INPUT)\n    private String id;\n\n    private String name;\n\n    @TableField(typeHandler = Fastjson2TypeHandler.class)\n    private Card card;\n\n    @TableField(typeHandler = Fastjson2TypeHandler.class)\n    private List<Attr> attr;\n\n    @TableField(typeHandler = Fastjson2TypeHandler.class)\n    private Map<String, Attr> attr2;\n\n    @TableField(typeHandler = Fastjson2TypeHandler.class)\n    private Map<String, String> attr3;\n\n    @TableField(typeHandler = Fastjson2TypeHandler.class)\n    private Map<String, Object> attr4;\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Attr {\n\n        private String name;\n\n        private String value;\n\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Card {\n\n        private String id;\n\n        private String value;\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJson2EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic interface FastJson2EntityMapper extends BaseMapper<FastJson2Entity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntity.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(value = \"t_fastjson_entity\", autoResultMap = true)\npublic class FastJsonEntity {\n\n    @TableId(type = IdType.INPUT)\n    private String id;\n\n    private String name;\n\n    @TableField(typeHandler = FastjsonTypeHandler.class)\n    private Card card;\n\n    @TableField(typeHandler = FastjsonTypeHandler.class)\n    private List<Attr> attr;\n\n    @TableField(typeHandler = FastjsonTypeHandler.class)\n    private Map<String, Attr> attr2;\n\n    @TableField(typeHandler = FastjsonTypeHandler.class)\n    private Map<String, String> attr3;\n\n    @TableField(typeHandler = FastjsonTypeHandler.class)\n    private Map<String, Object> attr4;\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Attr {\n\n        private String name;\n\n        private String value;\n\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Card {\n\n        private String id;\n\n        private String value;\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastJsonEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic interface FastJsonEntityMapper extends BaseMapper<FastJsonEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/Fastjson2Test.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic class Fastjson2Test extends BaseDbTest<FastJson2EntityMapper> {\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_fastjson2_entity\", \"CREATE TABLE IF NOT EXISTS t_fastjson2_entity (\" +\n            \"id VARCHAR(32) NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"card VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr2 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr3 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr4 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Test\n    void test() {\n        doTest(mapper -> {\n            var entity = new FastJson2Entity(\"123\", \"秋秋\", new FastJson2Entity.Card(\"1\", \"1111\"),\n                Arrays.asList(new FastJson2Entity.Attr(\"age\", \"18\"), new FastJson2Entity.Attr(\"sex\", \"女\")),\n                new HashMap<>(Map.of(\"test\", new FastJson2Entity.Attr(\"小红\", \"1\"))),\n                new HashMap<>(Map.of(\"name\", \"1\", \"test2\", \"2\")),\n                new HashMap<>(Map.of(\"test1\", \"1\", \"test2\", 2))\n            );\n            mapper.insert(entity);\n            FastJson2Entity jackJsonEntity = mapper.selectById(entity.getId());\n            Assertions.assertEquals(\"秋秋\", jackJsonEntity.getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());\n            Assertions.assertNotNull(jackJsonEntity.getCard());\n            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());\n            Assertions.assertEquals(\"小红\", jackJsonEntity.getAttr2().get(\"test\").getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());\n            Assertions.assertEquals(\"1\", jackJsonEntity.getAttr3().get(\"name\"));\n            Assertions.assertEquals(\"2\", jackJsonEntity.getAttr3().get(\"test2\"));\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get(\"test2\"));\n        });\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/FastjsonTest.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic class FastjsonTest extends BaseDbTest<FastJsonEntityMapper> {\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_fastjson_entity\", \"CREATE TABLE IF NOT EXISTS t_fastjson_entity (\" +\n            \"id VARCHAR(32) NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"card VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr2 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr3 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr4 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Test\n    void test() {\n        doTest(mapper -> {\n            var entity = new FastJsonEntity(\"123\", \"秋秋\", new FastJsonEntity.Card(\"1\", \"1111\"),\n                Arrays.asList(new FastJsonEntity.Attr(\"age\", \"18\"), new FastJsonEntity.Attr(\"sex\", \"女\")),\n                new HashMap<>(Map.of(\"test\", new FastJsonEntity.Attr(\"小红\", \"1\"))),\n                new HashMap<>(Map.of(\"name\", \"1\", \"test2\", \"2\")),\n                new HashMap<>(Map.of(\"test1\", \"1\", \"test2\", 2))\n            );\n            mapper.insert(entity);\n            FastJsonEntity jackJsonEntity = mapper.selectById(entity.getId());\n            Assertions.assertEquals(\"秋秋\", jackJsonEntity.getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());\n            Assertions.assertNotNull(jackJsonEntity.getCard());\n            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());\n            Assertions.assertEquals(\"小红\", jackJsonEntity.getAttr2().get(\"test\").getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());\n            Assertions.assertEquals(\"1\", jackJsonEntity.getAttr3().get(\"name\"));\n            Assertions.assertEquals(\"2\", jackJsonEntity.getAttr3().get(\"test2\"));\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get(\"test2\"));\n        });\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntity.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(value = \"t_gson_entity\", autoResultMap = true)\npublic class GsonEntity {\n\n    @TableId(type = IdType.INPUT)\n    private String id;\n\n    private String name;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Card card;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private List<Attr> attr;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Map<String, Attr> attr2;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Map<String, String> attr3;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Map<String, Object> attr4;\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Attr {\n\n        private String name;\n\n        private String value;\n\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Card {\n\n        private String id;\n\n        private String value;\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic interface GsonEntityMapper extends BaseMapper<GsonEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/GsonTest.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic class GsonTest extends BaseDbTest<GsonEntityMapper> {\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_gson_entity\", \"CREATE TABLE IF NOT EXISTS t_gson_entity (\" +\n            \"id VARCHAR(32) NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"card VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr2 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr3 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr4 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Test\n    void test() {\n        doTest(mapper -> {\n            var entity = new GsonEntity(\"123\", \"秋秋\", new GsonEntity.Card(\"1\", \"1111\"),\n                Arrays.asList(new GsonEntity.Attr(\"age\", \"18\"), new GsonEntity.Attr(\"sex\", \"女\")),\n                new HashMap<>(Map.of(\"test\", new GsonEntity.Attr(\"小红\", \"1\"))),\n                new HashMap<>(Map.of(\"name\", \"1\", \"test2\", \"2\")),\n                new HashMap<>(Map.of(\"test1\", \"1\", \"test2\", 2))\n            );\n            mapper.insert(entity);\n            GsonEntity jackJsonEntity = mapper.selectById(entity.getId());\n            Assertions.assertEquals(\"秋秋\", jackJsonEntity.getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());\n            Assertions.assertNotNull(jackJsonEntity.getCard());\n            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());\n            Assertions.assertEquals(\"小红\", jackJsonEntity.getAttr2().get(\"test\").getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());\n            Assertions.assertEquals(\"1\", jackJsonEntity.getAttr3().get(\"name\"));\n            Assertions.assertEquals(\"2\", jackJsonEntity.getAttr3().get(\"test2\"));\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());\n            //TODO 反序列化精度问题\n            Assertions.assertEquals(2.0, jackJsonEntity.getAttr4().get(\"test2\"));\n        });\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntity.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@TableName(value = \"t_jackson_entity\", autoResultMap = true)\npublic class JackJsonEntity {\n\n    @TableId(type = IdType.INPUT)\n    private String id;\n\n    private String name;\n\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Card card;\n\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private List<Attr> attr;\n\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Map<String, Attr> attr2;\n\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Map<String, String> attr3;\n\n    @TableField(typeHandler = JacksonTypeHandler.class)\n    private Map<String, Object> attr4;\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Attr {\n\n        private String name;\n\n        private String value;\n\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Card {\n\n        private String id;\n\n        private String value;\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JackJsonEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic interface JackJsonEntityMapper extends BaseMapper<JackJsonEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/json/JacksonTest.java",
    "content": "package com.baomidou.mybatisplus.test.json;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2024年3月4日\n */\npublic class JacksonTest extends BaseDbTest<JackJsonEntityMapper> {\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_jackson_entity\", \"CREATE TABLE IF NOT EXISTS t_jackson_entity (\" +\n            \"id VARCHAR(32) NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"card VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr2 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr3 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"attr4 VARCHAR(255) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Test\n    void test() {\n        doTest(mapper -> {\n            var entity = new JackJsonEntity(\"123\", \"秋秋\", new JackJsonEntity.Card(\"1\", \"1111\"),\n                Arrays.asList(new JackJsonEntity.Attr(\"age\", \"18\"), new JackJsonEntity.Attr(\"sex\", \"女\")),\n                new HashMap<>(Map.of(\"test\", new JackJsonEntity.Attr(\"小红\", \"1\"))),\n                new HashMap<>(Map.of(\"name\", \"1\", \"test2\", \"2\")),\n                new HashMap<>(Map.of(\"test1\", \"1\", \"test2\", 2))\n            );\n            mapper.insert(entity);\n            JackJsonEntity jackJsonEntity = mapper.selectById(entity.getId());\n            Assertions.assertEquals(\"秋秋\", jackJsonEntity.getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr().size());\n            Assertions.assertNotNull(jackJsonEntity.getCard());\n            Assertions.assertEquals(1, jackJsonEntity.getAttr2().size());\n            Assertions.assertEquals(\"小红\", jackJsonEntity.getAttr2().get(\"test\").getName());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr3().size());\n            Assertions.assertEquals(\"1\", jackJsonEntity.getAttr3().get(\"name\"));\n            Assertions.assertEquals(\"2\", jackJsonEntity.getAttr3().get(\"test2\"));\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().size());\n            Assertions.assertEquals(2, jackJsonEntity.getAttr4().get(\"test2\"));\n        });\n\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.logicdel;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    private String name;\n\n    @TableField(fill = FieldFill.UPDATE)\n    private String deleteBy;\n\n    @TableField(fill = FieldFill.UPDATE)\n    @TableLogic(delval = \"true\", value = \"false\")\n    private Boolean deleted;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.logicdel;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    @Select(\"select * from entity where id = #{id}\")\n    Entity byId(Long id);\n\n    int testDeleteBatch(@Param(Constants.COLL) List<Entity> entityList);\n\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/logicdel/LogicDelTest.java",
    "content": "package com.baomidou.mybatisplus.test.logicdel;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteBatchByIds;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class LogicDelTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void logicDel() {\n        doTestAutoCommit(i -> {\n            int delete = i.deleteById(1L);\n            assertThat(delete).isEqualTo(1);\n\n            delete = i.delete(Wrappers.<Entity>lambdaQuery().eq(Entity::getId, 2));\n            assertThat(delete).isEqualTo(1);\n        });\n\n        doTest(i -> {\n            Entity entity = i.byId(1L);\n            assertThat(entity).isNotNull();\n            assertThat(entity.getDeleted()).isTrue();\n\n            entity = i.byId(2L);\n            assertThat(entity).isNotNull();\n            assertThat(entity.getDeleted()).isTrue();\n        });\n\n        doTest(mapper -> {\n            Entity entity = new Entity();\n            entity.setName(\"测试根据实体删除\");\n            mapper.insert(entity);\n            assertThat(mapper.deleteById(entity)).isEqualTo(1);\n        });\n\n        doTest(mapper -> {\n            Entity entity1 = new Entity();\n            entity1.setName(\"测试根据实体主键批量删除\");\n            mapper.insert(entity1);\n            Entity entity2 = new Entity();\n            entity2.setName(\"测试根据实体主键批量删除\");\n            mapper.insert(entity2);\n            assertThat(mapper.deleteByIds(Arrays.asList(entity1.getId(), entity2.getId()))).isEqualTo(2);\n        });\n\n        doTest(mapper -> {\n            Entity entity1 = new Entity();\n            entity1.setName(\"测试根据实体批量删除\");\n            mapper.insert(entity1);\n            Entity entity2 = new Entity();\n            entity2.setName(\"测试根据实体批量删除\");\n            mapper.insert(entity2);\n            List<Entity> entityList = new ArrayList<>();\n            entityList.add(entity1);\n            entityList.add(entity2);\n            assertThat(mapper.deleteByIds(entityList)).isEqualTo(2);\n            entityList.forEach(entity -> {\n                //TODO 3.5.7 修改为使用IN的方式删除而不是使用行记录删除.\n//                Assertions.assertEquals(\"聂秋秋\", entity.getDeleteBy());\n            });\n        });\n\n        doTest(mapper -> {\n            Entity entity1 = new Entity();\n            entity1.setName(\"测试自定义方法根据实体批量删除\");\n            mapper.insert(entity1);\n            Entity entity2 = new Entity();\n            entity2.setName(\"测试自定义方法根据实体批量删除\");\n            mapper.insert(entity2);\n            List<Entity> entityList = new ArrayList<>();\n            entityList.add(entity1);\n            entityList.add(entity2);\n            assertThat(mapper.testDeleteBatch(entityList)).isEqualTo(2);\n            entityList.forEach(entity -> {\n                Assertions.assertEquals(\"聂秋秋\", entity.getDeleteBy());\n            });\n        });\n\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"delete_by VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"deleted BOOLEAN NOT NULL DEFAULT false,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n    @Override\n    protected GlobalConfig globalConfig() {\n        GlobalConfig globalConfig = super.globalConfig();\n        globalConfig.setMetaObjectHandler(new MetaObjectHandler() {\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                strictUpdateFill(metaObject, \"deleteBy\", String.class, \"聂秋秋\");\n            }\n        });\n        globalConfig.setSqlInjector(new DefaultSqlInjector() {\n            @Override\n            public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n                List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);\n                methodList.add(new LogicDeleteBatchByIds(\"testDeleteBatch\"));\n                return methodList;\n            }\n        });\n        return globalConfig;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/AppConfig.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.h2.Driver;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.mybatis.spring.annotation.MapperScans;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport javax.sql.DataSource;\n\n/**\n * @author nieqiurong\n */\n@ComponentScan(\"com.baomidou.mybatisplus.test.multisqlsessionfactory\")\n@MapperScans(\n    {\n        @MapperScan(value = \"com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper\", sqlSessionFactoryRef = \"sqlSessionFactory1\"),\n        @MapperScan(value = \"com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper\", sqlSessionFactoryRef = \"sqlSessionFactory2\")\n    }\n)\n@Configuration\npublic class AppConfig {\n\n    @Bean\n    public DataSource dataSourceA() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:testa;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public DataSourceTransactionManager transactionManagerA(DataSource dataSourceA) {\n        return new DataSourceTransactionManager(dataSourceA);\n    }\n\n    @Bean\n    public TransactionTemplate transactionTemplateA(DataSourceTransactionManager transactionManagerA) {\n        return new TransactionTemplate(transactionManagerA);\n    }\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory1(DataSource dataSourceA) throws Exception {\n        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();\n        mybatisSqlSessionFactoryBean.setDataSource(dataSourceA);\n        mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration());\n        return mybatisSqlSessionFactoryBean.getObject();\n    }\n\n    @Bean\n    public DataSource dataSourceB() {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:h2:mem:testb;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        dataSource.setUsername(\"sa\");\n        dataSource.setPassword(\"\");\n        return dataSource;\n    }\n\n    @Bean\n    public DataSourceTransactionManager transactionManagerB(DataSource dataSourceB) {\n        return new DataSourceTransactionManager(dataSourceB);\n    }\n\n    @Bean\n    public TransactionTemplate transactionTemplateB(DataSourceTransactionManager transactionManagerB) {\n        return new TransactionTemplate(transactionManagerB);\n    }\n\n\n    @Bean\n    public SqlSessionFactory sqlSessionFactory2(DataSource dataSourceB) throws Exception {\n        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();\n        mybatisSqlSessionFactoryBean.setDataSource(dataSourceB);\n        mybatisSqlSessionFactoryBean.setConfiguration(new MybatisConfiguration());\n        return mybatisSqlSessionFactoryBean.getObject();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/MultiSqlSessionFactoryTest.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory;\n\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.AEntityService;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.BEntityService;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport javax.sql.DataSource;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(classes = {AppConfig.class})\npublic class MultiSqlSessionFactoryTest {\n\n    @Autowired\n    private AEntityService aEntityService;\n\n    @Autowired\n    private BEntityService bEntityService;\n\n    @Autowired\n    private DataSource dataSourceA;\n\n    @Autowired\n    private DataSource dataSourceB;\n\n    @Test\n    void test() throws Exception {\n        new SqlRunner(dataSourceA.getConnection()).run(\n            \"\"\"\n                   CREATE TABLE IF NOT EXISTS  t_entity_a (\n                                           \tid BIGINT NOT NULL AUTO_INCREMENT,\n                                           \tname VARCHAR(30) NULL DEFAULT NULL ,\n                                           \tPRIMARY KEY (id)\n                                           );\n                \"\"\"\n        );\n\n        new SqlRunner(dataSourceB.getConnection()).run(\n            \"\"\"\n                   CREATE TABLE IF NOT EXISTS  t_entity_b (\n                                           \tid BIGINT NOT NULL AUTO_INCREMENT,\n                                           \tname VARCHAR(30) NULL DEFAULT NULL ,\n                                           \tPRIMARY KEY (id)\n                                           );\n                \"\"\"\n        );\n        Assertions.assertEquals(0L, aEntityService.count());\n        Assertions.assertEquals(0L, bEntityService.count());\n        aEntityService.testSaveBath(List.of(new AEntity(\"test1\"), new AEntity(\"test2\")));\n        Assertions.assertEquals(2L, aEntityService.count());\n        bEntityService.testSaveBath(List.of(new BEntity(\"test1\"), new BEntity(\"test2\"), new BEntity(\"test3\")));\n        Assertions.assertEquals(3L, bEntityService.count());\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/entity/AEntity.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\n@TableName(\"t_entity_a\")\npublic class AEntity {\n\n    private Long id;\n\n    private String name;\n\n    public AEntity() {\n    }\n\n    public AEntity(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/mapper/AEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author nieqiurong\n */\n@Mapper\npublic interface AEntityMapper extends BaseMapper<AEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/service/AEntityService.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\npublic interface AEntityService extends IService<AEntity> {\n\n    void testSaveBath(List<AEntity> aEntityList);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/a/service/impl/AEntityServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.entity.AEntity;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.mapper.AEntityMapper;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.a.service.AEntityService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@Service\npublic class AEntityServiceImpl extends ServiceImpl<AEntityMapper, AEntity> implements AEntityService {\n\n    @Autowired\n    protected TransactionTemplate transactionTemplateA;\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class, transactionManager = \"transactionManagerA\")\n    public void testSaveBath(List<AEntity> aEntityList) {\n        transactionTemplateA.execute((c) -> {\n            this.saveBatch(aEntityList);\n            return null;\n        });\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/entity/BEntity.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\n@TableName(\"t_entity_b\")\npublic class BEntity {\n\n    private Long id;\n\n    private String name;\n\n    public BEntity() {\n    }\n\n    public BEntity(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/mapper/BEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author nieqiurong\n */\n@Mapper\npublic interface BEntityMapper extends BaseMapper<BEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/service/BEntityService.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\npublic interface BEntityService extends IService<BEntity> {\n\n    void testSaveBath(List<BEntity> bEntityList);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/multisqlsessionfactory/b/service/impl/BEntityServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.entity.BEntity;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.mapper.BEntityMapper;\nimport com.baomidou.mybatisplus.test.multisqlsessionfactory.b.service.BEntityService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\n@Service\npublic class BEntityServiceImpl extends ServiceImpl<BEntityMapper, BEntity> implements BEntityService {\n\n    @Autowired\n    private TransactionTemplate transactionTemplateB;\n\n    @Override\n    @Transactional(rollbackFor = RuntimeException.class, transactionManager = \"transactionManagerB\")\n    public void testSaveBath(List<BEntity> bEntityList) {\n        transactionTemplateB.execute((c) -> {\n            saveBatch(bEntityList);\n            return null;\n        });\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.non;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    @TableId(\"t_id\")\n    private Long id;\n\n    @TableField(\"t_name\")\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.non;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Select;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    @Select(\"select * from entity where id = #{id}\")\n    Entity byId(Long id);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/non/NonTest.java",
    "content": "package com.baomidou.mybatisplus.test.non;\n\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class NonTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        doTest(i -> {\n            Entity entity = i.selectOne(Wrappers.<Entity>lambdaQuery().eq(Entity::getId, 1));\n            assertThat(entity).isNotNull();\n            assertThat(entity.getName()).isNotNull();\n\n            entity.setName(\"老吴\");\n            int update = i.updateById(entity);\n            assertThat(update).isEqualTo(1);\n        });\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(t_id,t_name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"t_id BIGINT NOT NULL,\" +\n            \"t_name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (t_id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.optimisticlocker;\n\nimport com.baomidou.mybatisplus.annotation.Version;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\n@Data\n@Accessors(chain = true)\npublic class Entity {\n\n    private Long id;\n\n    private String name;\n\n    @Version\n    private Integer version;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.optimisticlocker;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/optimisticlocker/OptimisticLockerTest.java",
    "content": "package com.baomidou.mybatisplus.test.optimisticlocker;\n\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\npublic class OptimisticLockerTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        doTestAutoCommit(m -> {\n            Entity entity = new Entity().setId(1L).setName(\"老王\");\n            int updateById = m.updateById(entity);\n            assertThat(updateById).as(\"更新成功\").isEqualTo(1);\n\n            entity = m.selectById(1L);\n            assertThat(entity.getVersion()).as(\"因为没给version赋值就没走插件\").isEqualTo(0);\n\n            updateById = m.updateById(entity);\n            assertThat(updateById).as(\"更新成功\").isEqualTo(1);\n\n            entity = m.selectById(1L);\n            assertThat(entity.getVersion()).as(\"因为version有值就走了插件\").isEqualTo(1);\n        });\n\n\n        doTestAutoCommit(m -> {\n            int update = m.update(new Entity().setName(\"老王\"), Wrappers.<Entity>query().eq(\"id\", 2));\n            assertThat(update).as(\"更新成功\").isEqualTo(1);\n\n            Entity entity = m.selectById(2L);\n            assertThat(entity.getVersion()).as(\"因为没给version赋值就没走插件\").isEqualTo(0);\n\n            update = m.update(new Entity().setName(\"老王\").setVersion(0), Wrappers.<Entity>query().eq(\"id\", 2));\n            assertThat(update).as(\"更新成功\").isEqualTo(1);\n\n            entity = m.selectById(2L);\n            assertThat(entity.getVersion()).as(\"因为version有值就走了插件\").isEqualTo(1);\n        });\n    }\n\n    @Test\n    void testBatch() {\n        var entity1 = new Entity().setName(\"testBatch\").setVersion(1);\n        var entity2 = new Entity().setName(\"testBatch\").setVersion(1);\n        var entity3 = new Entity().setName(\"testBatch\").setVersion(1);\n        var entityList = List.of(entity1, entity2, entity3);\n        doTest(mapper -> {\n            Assertions.assertTrue(SqlHelper.retBool(mapper.insert(entityList)));\n            Assertions.assertTrue(SqlHelper.retBool(mapper.updateById(entityList)));\n            entity2.setVersion(6);\n            Assertions.assertFalse(SqlHelper.retBool(mapper.updateById(entityList)));\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity (id,name) values (1,'1'),(2,'2'),(3,'3')\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"version integer not NULL default 0,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByEntity.java",
    "content": "package com.baomidou.mybatisplus.test.orderby;\n\nimport com.baomidou.mybatisplus.annotation.OrderBy;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\n@TableName(value = \"t_order_test\")\npublic class OrderByEntity {\n\n    @OrderBy(sort = 3)\n    @TableId(value = \"oid\")\n    private Long id;\n\n    private String name;\n\n    @OrderBy(asc = true, sort = 2)\n    @TableField(\"nl\")\n    private Long age;\n\n    @OrderBy\n    private String tel;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByMapper.java",
    "content": "package com.baomidou.mybatisplus.test.orderby;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong\n */\npublic interface OrderByMapper extends BaseMapper<OrderByEntity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/orderby/OrderByMapperTest.java",
    "content": "package com.baomidou.mybatisplus.test.orderby;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author nieqiurong\n */\npublic class OrderByMapperTest extends BaseDbTest<OrderByMapper> {\n    @Test\n    void test() {\n        doTest(mapper -> {\n            mapper.selectList(Wrappers.emptyWrapper());\n\n            mapper.selectList(Wrappers.<OrderByEntity>lambdaQuery().select(OrderByEntity::getName));\n        });\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(OrderByEntity.class);\n        for (OrderFieldInfo orderByField : tableInfo.getOrderByFields()) {\n            if (\"oid\".equals(orderByField.getColumn())) {\n                Assertions.assertEquals(3, orderByField.getSort());\n                Assertions.assertEquals(Constants.DESC, orderByField.getType());\n            } else if (\"age\".equals(orderByField.getColumn())) {\n                Assertions.assertEquals(2, orderByField.getSort());\n                Assertions.assertEquals(Constants.ASC, orderByField.getType());\n            } else if (\"tel\".equals(orderByField.getColumn())) {\n                Assertions.assertEquals(Short.MAX_VALUE, orderByField.getSort());\n                Assertions.assertEquals(Constants.DESC, orderByField.getType());\n            }\n        }\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into t_order_test(oid,name,nl,tel) values(1,'demo1',12, '13311111111'),(2,'demo2',13, '13322222222'),(3,'demo3',14, '13333333333');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_order_test\", \"CREATE TABLE IF NOT EXISTS t_order_test (\" +\n            \"oid BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"tel VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"nl INT NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (oid))\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/PhoenixBaseMapper.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author: fly\n * Created date: 2019/12/21 16:35\n */\npublic interface PhoenixBaseMapper<T> extends BaseMapper<T> {\n\n    int upsert(T entity);\n\n    @Override\n    default int insert(T entity) {\n        return upsert(entity);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/PhoenixTest.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport com.baomidou.mybatisplus.test.phoenix.entity.PhoenixTestInfo;\nimport com.baomidou.mybatisplus.test.phoenix.mapper.PhoenixTestInfoMapper;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlMethod.UPSERT_ONE;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author: fly\n * Created date: 2019/12/21 16:35\n */\n@DirtiesContext\n@TestMethodOrder(MethodOrderer.MethodName.class)\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:phoenix/spring-test-phoenix.xml\"})\npublic class PhoenixTest {\n\n    @Autowired\n    PhoenixTestInfoMapper mapper;\n\n    @Test\n    void a00_upsertForeach() {\n        int size = 10;\n        for (int i = 0; i < 10; i++) {\n            PhoenixTestInfo testInfo = new PhoenixTestInfo()\n                .setId(i)\n                .setName(\"test\");\n            assertEquals (1, mapper.upsert(testInfo));\n        }\n\n        assertEquals(size, mapper.selectList(Wrappers.emptyWrapper()).size());\n    }\n\n\n    @Test\n    void a01_upsertBatch() {\n        int size = 10000;\n        try (SqlSession sqlSession = SqlHelper.sqlSessionBatch(PhoenixTestInfo.class)) {\n            String sqlStatement = SqlHelper.table(PhoenixTestInfo.class).getSqlStatement(UPSERT_ONE.getMethod());\n            for (int i = 0; i < size; i++) {\n                PhoenixTestInfo testInfo = new PhoenixTestInfo()\n                    .setId(i)\n                    .setName(\"upsertBatch\");\n                sqlSession.insert(sqlStatement, testInfo);\n            }\n            sqlSession.commit();\n//            sqlSession.flushStatements();\n        }\n        assertEquals(size, mapper.selectList(Wrappers.emptyWrapper()).size());\n    }\n\n    @Test\n    void a02_page() {\n        Page<PhoenixTestInfo> page = new Page<>(2, 100);\n        IPage<PhoenixTestInfo> result = mapper.selectPage(page, Wrappers.emptyWrapper());\n\n        assertEquals(10000, result.getTotal());\n        assertEquals(2, result.getCurrent());\n        assertEquals(100, result.getPages());\n        assertEquals(100, result.getRecords().size());\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/config/DBConfig.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\nimport org.springframework.jdbc.datasource.init.DataSourceInitializer;\nimport org.springframework.jdbc.datasource.init.DatabasePopulator;\nimport org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.sql.Driver;\nimport java.util.Properties;\n\n/**\n * @author miemie\n * @since 2018/6/7\n */\n@Configuration\n@SuppressWarnings(\"unchecked\")\n@EnableTransactionManagement\npublic class DBConfig {\n\n    @Bean(\"dataSource\")\n    public DataSource dataSource() throws ClassNotFoundException {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriverClass((Class<? extends Driver>) Class.forName(\"org.apache.phoenix.jdbc.PhoenixDriver\"));\n        dataSource.setUrl(\"jdbc:phoenix:dnode28,dnode29,dnode30:2181\");\n        Properties properties = new Properties();\n        properties.setProperty(\"schema\", \"TEST\");\n        dataSource.setConnectionProperties(properties);\n        return dataSource;\n    }\n\n    @Bean\n    public DataSourceTransactionManager transactionManager(DataSource ds) {\n        return new DataSourceTransactionManager(ds);\n    }\n\n    @Bean\n    public DataSourceInitializer dataSourceInitializer(DataSource dataSource) throws IOException {\n        final DataSourceInitializer initializer = new DataSourceInitializer();\n        initializer.setDataSource(dataSource);\n        initializer.setDatabasePopulator(databasePopulator());\n        initializer.setEnabled(true);\n        return initializer;\n    }\n\n    private DatabasePopulator databasePopulator() throws IOException {\n        ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();\n        resourceDatabasePopulator.setContinueOnError(false);\n        resourceDatabasePopulator.addScripts(\n            new PathMatchingResourcePatternResolver().getResources(\"classpath:/phoenix/*.sql\")\n        );\n        return resourceDatabasePopulator;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/config/MybatisPlusConfig.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix.config;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;\nimport com.baomidou.mybatisplus.extension.injector.methods.Upsert;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.sql.DataSource;\nimport java.util.List;\n\n/**\n * Mybatis Plus Config\n *\n * @author Caratacus\n * @since 2017/4/1\n */\n@Configuration\n@MapperScan(\"com.baomidou.mybatisplus.test.phoenix.mapper\")\npublic class MybatisPlusConfig {\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(\n        DataSource dataSource,\n        GlobalConfig globalConfig\n    ) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        /* 数据源 */\n        sqlSessionFactory.setDataSource(dataSource);\n        /* xml扫描 */\n//        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()\n//            .getResources(\"classpath:/mapper/*.xml\"));\n        /* 扫描 typeHandler */\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        /* 驼峰转下划线 */\n        configuration.setMapUnderscoreToCamelCase(true);\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n        /* map 下划线转驼峰 */\n        configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());\n        sqlSessionFactory.setConfiguration(configuration);\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n\n    @Bean\n    public GlobalConfig globalConfig() {\n        GlobalConfig conf = new GlobalConfig();\n//        conf.setDbConfig(new GlobalConfig.DbConfig()\n//            .setColumnFormat(\"`%s`\"));\n        DefaultSqlInjector phoenixSqlInjector = new DefaultSqlInjector() {\n            /**\n             * 注入自定义全局方法\n             */\n            @Override\n            public List<AbstractMethod> getMethodList(org.apache.ibatis.session.Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n                List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);\n                methodList.add(new Upsert());\n                return methodList;\n            }\n        };\n        conf.setSqlInjector(phoenixSqlInjector);\n        return conf;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/entity/PhoenixTestInfo.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n/**\n * @author fly\n * @date 2019/5/22 17:52\n * description: phoenix hbase TEST_INFO table\n */\n@Data\n@Accessors(chain = true)\n@TableName(\"test_info\")\npublic class PhoenixTestInfo {\n\n    /**\n     * Phoenix主键Mybatis不支持自增，需要插入传入或者设置key generator\n     */\n    private Integer id;\n\n    private String name;\n    private String phone;\n    private String position;\n    private String department;\n    private String company;\n    private String fileName;\n    private String posDepCom;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/phoenix/mapper/PhoenixTestInfoMapper.java",
    "content": "package com.baomidou.mybatisplus.test.phoenix.mapper;\n\nimport com.baomidou.mybatisplus.test.phoenix.PhoenixBaseMapper;\nimport com.baomidou.mybatisplus.test.phoenix.entity.PhoenixTestInfo;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Param;\n\n/**\n * @author fly\n * @date 2019/5/22 17:55\n * description:\n */\npublic interface PhoenixTestInfoMapper extends PhoenixBaseMapper<PhoenixTestInfo> {\n\n        @Insert(\n                \"UPSERT INTO TEST_INFO \" +\n                \"(ID, NAME) \" +\n                \"VALUES(#{t.id},#{t.name})\"\n        )\n        void addTestInfo(@Param(\"t\") PhoenixTestInfo t);\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\")) {\n            JerryParser jerryParser = Jerry.create((new LagartoDOMBuilder().enableXmlMode()));\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency extension = dependenciesMap.get(\"mybatis-plus-spring\");\n            Assertions.assertEquals(\"compile\", extension.getScope());\n            Assertions.assertFalse(extension.isOptional());\n            Dependency mybatisSpring = dependenciesMap.get(\"mybatis-spring\");\n            Assertions.assertEquals(\"compile\", mybatisSpring.getScope());\n            Assertions.assertTrue(mybatisSpring.isOptional());\n            Dependency kotlinStdlib = dependenciesMap.get(\"kotlin-stdlib-jdk8\");\n            Assertions.assertEquals(\"compile\", kotlinStdlib.getScope());\n            Assertions.assertTrue(kotlinStdlib.isOptional());\n            Dependency jsqlparserLib = dependenciesMap.get(\"mybatis-plus-jsqlparser-4.9\");\n            Assertions.assertEquals(\"compile\", jsqlparserLib.getScope());\n            Assertions.assertTrue(jsqlparserLib.isOptional());\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/PostgresqlTest.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql;\n\nimport com.baomidou.mybatisplus.test.postgresql.entity.Pgtable;\nimport com.baomidou.mybatisplus.test.postgresql.mapper.PgtableMappper;\nimport com.baomidou.mybatisplus.test.postgresql.service.IPgtableService;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.test.context.ContextConfiguration;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\n\nimport java.util.List;\n\n@ExtendWith(SpringExtension.class)\n@ContextConfiguration(locations = {\"classpath:postgresql/spring-test-postgresql.xml\"})\npublic class PostgresqlTest {\n    @Autowired\n    private PgtableMappper mapper;\n    @Autowired\n    private IPgtableService pgtableService;\n\n    @Test\n    void test() {\n        Pgtable pgtable = Pgtable.builder().compName(\"test\").age(10).build();\n        Assertions.assertTrue(mapper.insert(pgtable) > 0);\n        System.out.println(pgtable.getId());\n\n        List<Pgtable> pgtableList = mapper.selectList(null);\n        Assertions.assertNotNull(pgtableList);\n        Assertions.assertTrue(pgtableList.size() > 0);\n    }\n\n    @Test\n    void testTask() throws InterruptedException {\n        // 测试线程\n        pgtableService.testTask();\n        // 主线程休眠\n        Thread.sleep(10000);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/config/PostgresqlConfig.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql.config;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.type.JdbcType;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.postgresql.Driver;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@SuppressWarnings(\"unchecked\")\n@EnableTransactionManagement\n@MapperScan(\"com.baomidou.mybatisplus.test.postgresql.mapper\")\npublic class PostgresqlConfig {\n\n    @Bean(\"dataSource\")\n    public DataSource dataSource() throws ClassNotFoundException {\n        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();\n        dataSource.setDriver(new Driver());\n        dataSource.setUrl(\"jdbc:postgresql://localhost:5432/test\");\n        dataSource.setUsername(\"postgres\");\n        dataSource.setPassword(\"123456\");\n        return dataSource;\n    }\n\n\n    @Bean(\"mybatisSqlSession\")\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();\n        /* 数据源 */\n        sqlSessionFactory.setDataSource(dataSource);\n        /* 扫描 typeHandler */\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setJdbcTypeForNull(JdbcType.NULL);\n        /* 驼峰转下划线 */\n        configuration.setMapUnderscoreToCamelCase(true);\n        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();\n        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor);\n        sqlSessionFactory.setConfiguration(configuration);\n        GlobalConfig globalConfig = new GlobalConfig();\n        GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();\n        // TODO 这里申明所有字段为大写\n        dbConfig.setCapitalMode(true);\n        dbConfig.setColumnFormat(\"\\\"%s\\\"\");\n        globalConfig.setDbConfig(dbConfig);\n        sqlSessionFactory.setGlobalConfig(globalConfig);\n        return sqlSessionFactory.getObject();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/entity/Pgtable.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Builder;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\n@Builder\npublic class Pgtable {\n    @TableId(type = IdType.AUTO)\n    private Long id;\n    private String compName;\n    private Integer age;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/mapper/PgtableMappper.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.postgresql.entity.Pgtable;\n\npublic interface PgtableMappper extends BaseMapper<Pgtable> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/service/IPgtableService.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.test.postgresql.entity.Pgtable;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic interface IPgtableService extends IService<Pgtable> {\n\n    void testTask();\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/postgresql/service/impl/PgtableServiceImpl.java",
    "content": "package com.baomidou.mybatisplus.test.postgresql.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.baomidou.mybatisplus.test.postgresql.entity.Pgtable;\nimport com.baomidou.mybatisplus.test.postgresql.mapper.PgtableMappper;\nimport com.baomidou.mybatisplus.test.postgresql.service.IPgtableService;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n@Service\npublic class PgtableServiceImpl extends ServiceImpl<PgtableMappper, Pgtable> implements IPgtableService {\n\n    @Override\n    public void testTask() {\n        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1);\n        executorService.schedule(() -> {\n            try {\n                this.updateById(Pgtable.builder().id(1L).compName(\"testTask01\").age(11).build());\n                baseMapper.updateById(Pgtable.builder().id(1L).compName(\"testTask02\").age(12).build());\n            } catch (Exception exception) {\n                exception.getStackTrace();\n            } finally {\n                executorService.shutdown();\n            }\n        }, 3, TimeUnit.SECONDS);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/A.java",
    "content": "package com.baomidou.mybatisplus.test.puginsome;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2022-05-11\n */\n@Data\npublic class A {\n    private int id;\n    private String name;\n\n    @TableField(exist = false)\n    private B b;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/AMapper.java",
    "content": "package com.baomidou.mybatisplus.test.puginsome;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2022-05-11\n */\npublic interface AMapper extends BaseMapper<A> {\n\n    List<A> list();\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/B.java",
    "content": "package com.baomidou.mybatisplus.test.puginsome;\n\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2022-05-11\n */\n@Data\npublic class B {\n    private int id;\n    private int aId;\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/BMapper.java",
    "content": "package com.baomidou.mybatisplus.test.puginsome;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2022-05-11\n */\npublic interface BMapper extends BaseMapper<B> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/puginsome/PluginSomeTest.java",
    "content": "package com.baomidou.mybatisplus.test.puginsome;\n\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\nimport org.junit.jupiter.api.Test;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\npublic class PluginSomeTest extends BaseDbTest<AMapper> {\n\n    @Test\n    void test() {\n        doTest(m -> {\n            List<A> a = m.list();\n            a.forEach(System.out::println);\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new InnerInterceptor() {\n            @Override\n            public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n                System.out.println(\"willDoQuery\");\n                return true;\n            }\n\n            @Override\n            public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n                System.out.println(\"beforeQuery\");\n            }\n\n            @Override\n            public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {\n                System.out.println(\"willDoUpdate\");\n                return true;\n            }\n\n            @Override\n            public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {\n                System.out.println(\"beforeUpdate\");\n            }\n\n            @Override\n            public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n                System.out.println(\"beforePrepare\");\n            }\n\n            @Override\n            public void beforeGetBoundSql(StatementHandler sh) {\n                System.out.println(\"beforeGetBoundSql\");\n            }\n        });\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected List<Class<?>> otherMapper() {\n        return Collections.singletonList(BMapper.class);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into a (id,name) values (1,'1'),(2,'2');\" +\n            \"insert into b (id,a_id,name) values (1,'1','1-1'),(2,'2','2-2')\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists a\",\n            \"CREATE TABLE IF NOT EXISTS a (\\n\" +\n                \"id integer NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\", \"drop table if exists b\",\n            \"CREATE TABLE IF NOT EXISTS b (\\n\" +\n                \"id integer NOT NULL,\\n\" +\n                \"a_id integer NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntity.java",
    "content": "package com.baomidou.mybatisplus.test.record;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\n/**\n * @author nieqiurong 2023年9月14日\n */\n@TableName(value = \"t_record\")\npublic record RecordEntity(Long id,\n                           String name, @TableField(value = \"tel\") String phone) {\n\n\n}\n\n\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.record;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author nieqiurong 2023年9月14日\n */\npublic interface RecordEntityMapper extends BaseMapper<RecordEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/record/RecordEntityTest.java",
    "content": "package com.baomidou.mybatisplus.test.record;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n\n/**\n * @author nieqiurong 2023年9月14日\n */\npublic class RecordEntityTest extends BaseDbTest<RecordEntityMapper> {\n\n    @Test\n    void testInsert() {\n        doTest(mapper -> {\n            //TODO 无法使用自动填充与主键生成,不建议充当PO使用\n            RecordEntity recordEntity = new RecordEntity(123L, \"苗人凤\", \"13388888888\");\n            Assertions.assertEquals(1, mapper.insert(recordEntity));\n        });\n    }\n\n    @Test\n    void testUpdate() {\n        doTest(mapper -> {\n            RecordEntity recordEntity = new RecordEntity(2L, \"苗人凤2\", null);\n            Assertions.assertEquals(1, mapper.updateById(recordEntity));\n            recordEntity = mapper.selectById(2L);\n            Assertions.assertEquals(\"苗人凤2\", recordEntity.name());\n            Assertions.assertNotNull(recordEntity.phone(), \"13322222222\");\n        });\n    }\n\n    @Test\n    void testSelect() {\n        doTest(mapper -> {\n            RecordEntity recordEntity = mapper.selectById(3L);\n            Assertions.assertEquals(\"demo3\", recordEntity.name());\n            Assertions.assertNotNull(recordEntity.phone(), \"13333333333\");\n        });\n    }\n\n    @Test\n    void testDelete() {\n        doTest(mapper -> Assertions.assertEquals(1, mapper.deleteById(new RecordEntity(1L, \"-----\", \"1\"))));\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into t_record(id,name,tel) values(1,'demo1','13311111111'),(2,'demo2','13322222222'),(3,'demo3','13333333333');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists t_record\", \"CREATE TABLE IF NOT EXISTS t_record (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"tel VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/reflection/ExampleObjectFactory.java",
    "content": "package com.baomidou.mybatisplus.test.reflection;\n\nimport org.apache.ibatis.reflection.factory.DefaultObjectFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * 对象工厂\n * @author nieqiurong 2018/8/14 13:12.\n */\npublic class ExampleObjectFactory extends DefaultObjectFactory {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = -2878759377109110945L;\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExampleObjectFactory.class);\n\n    public <T> T create(Class<T> type) {\n        LOGGER.debug(\"生成一个对象 type = [\" + type + \"]\");\n        return super.create(type);\n    }\n\n    @Override\n    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {\n        LOGGER.info(\"生成一个对象 type = [\" + type + \"], constructorArgTypes = [\" + constructorArgTypes + \"], constructorArgs = [\" + constructorArgs + \"]\");\n        return super.create(type, constructorArgTypes, constructorArgs);\n    }\n\n    public void setProperties(Properties properties) {\n        LOGGER.debug(\"设置属性 properties = [\" + properties + \"]\");\n        super.setProperties(properties);\n    }\n    public <T> boolean isCollection(Class<T> type) {\n        return Collection.class.isAssignableFrom(type);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.replaceplaceholder;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    @TableField(\"`name`\")\n    private String name;\n\n    private Integer age;\n\n    @TableField(exist = false)\n    private EntitySub es;\n\n    @Data\n    public static class EntitySub {\n        private Long id;\n        private String name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.replaceplaceholder;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    @Select(\"select {@entity-name,id} from entity\")\n    List<Entity> selectAll();\n\n    @Select(\"select {@entity:e},{@entity_sub-id:es:es} from entity e left join entity_sub es on e.id = es.id\")\n    List<Entity> selectAll2();\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/EntitySubMapper.java",
    "content": "package com.baomidou.mybatisplus.test.replaceplaceholder;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntitySubMapper extends BaseMapper<Entity.EntitySub> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/replaceplaceholder/ReplacePlaceholderTest.java",
    "content": "package com.baomidou.mybatisplus.test.replaceplaceholder;\n\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.ReplacePlaceholderInnerInterceptor;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class ReplacePlaceholderTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void replace() {\n        doTest(i -> {\n            List<Entity> list = i.selectAll();\n            System.out.println(list);\n            assertThat(list.getFirst().getId()).isNull();\n            assertThat(list.getFirst().getName()).isNull();\n            assertThat(list.getFirst().getAge()).isNotNull();\n            list = i.selectAll2();\n            System.out.println(list);\n            assertThat(list.getFirst().getEs().getId()).isNull();\n            assertThat(list.getFirst().getEs().getName()).isNotBlank();\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new ReplacePlaceholderInnerInterceptor(\"\\\"\"));\n        return Collections.singletonList(interceptor);\n    }\n\n    // 全局 gradle test 不支持,全局缓存串台了\n//    @Override\n//    protected GlobalConfig globalConfig() {\n//        GlobalConfig config = super.globalConfig();\n//        config.getDbConfig().setReplacePlaceholder(true);\n//        return config;\n//    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name,age) values(1,'1',1),(2,'2',2);\" +\n            \"insert into entity_sub(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"drop table if exists entity_sub\",\n            \"CREATE TABLE IF NOT EXISTS entity (\" +\n                \"id BIGINT NOT NULL,\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n                \"age int NULL DEFAULT NULL,\" +\n                \"PRIMARY KEY (id))\",\n            \"CREATE TABLE IF NOT EXISTS entity_sub (\" +\n                \"id BIGINT NOT NULL,\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n                \"PRIMARY KEY (id))\");\n    }\n\n    @Override\n    protected List<Class<?>> otherMapper() {\n        return List.of(EntitySubMapper.class);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.resultmap;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\n@Accessors(chain = true)\n@TableName(resultMap = \"resultMap\")\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Gg gg1;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private Gg gg2;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private List<Gg> gg3;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private List<Gg4> gg4;\n\n    @TableField(typeHandler = GsonTypeHandler.class)\n    private String[] str;\n\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Gg {\n        private String name;\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class Gg4 {\n        private String name;\n        private Gg gg;\n        private List<Gg> ggList;\n        private Map<String,Gg> ggMap;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.resultmap;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/resultmap/ResultMapTest.java",
    "content": "package com.baomidou.mybatisplus.test.resultmap;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class ResultMapTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        doTestAutoCommit(m -> m.insert(new Entity().setGg1(new Entity.Gg(\"老王\"))\n            .setStr(new String[]{\"hello\"})\n            .setGg2(new Entity.Gg(\"老李\"))\n            .setGg3(List.of(new Entity.Gg(\"老张\")))\n            .setGg4(List.of(new Entity.Gg4(\"秋秋\", new Entity.Gg(\"小红\"), List.of(new Entity.Gg(\"小猫\")), Map.of(\"test\", new Entity.Gg(\"小明\")))))\n        ));\n\n        doTest(m -> {\n            Entity entity = m.selectOne(null);\n            assertThat(entity).as(\"插入正常\").isNotNull();\n            assertThat(entity.getGg1()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getGg1().getName()).as(\"是老王\").isEqualTo(\"老王\");\n\n            assertThat(entity.getGg2()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getGg2().getName()).as(\"是老李\").isEqualTo(\"老李\");\n\n            assertThat(entity.getGg3()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getGg3().getFirst().getName()).as(\"是老张\").isEqualTo(\"老张\");\n\n            assertThat(entity.getGg4()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getGg4().getFirst().getName()).as(\"是秋秋\").isEqualTo(\"秋秋\");\n            assertThat(entity.getGg4().getFirst().getGg().getName()).as(\"是小红\").isEqualTo(\"小红\");\n            assertThat(entity.getGg4().getFirst().getGgList().getFirst().getName()).as(\"是小猫\").isEqualTo(\"小猫\");\n            assertThat(entity.getGg4().getFirst().getGgMap().get(\"test\").getName()).as(\"是小明\").isEqualTo(\"小明\");\n\n            assertThat(entity.getStr()).as(\"typeHandler正常\").isNotNull();\n            assertThat(entity.getStr()[0]).isEqualTo(\"hello\");\n        });\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"gg1 VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"gg2 VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"gg3 VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"gg4 VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"str VARCHAR(255) NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n\n    @Override\n    protected String mapperXml() {\n        return \"com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.rewrite;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.rewrite;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/rewrite/RewriteTest.java",
    "content": "package com.baomidou.mybatisplus.test.rewrite;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic class RewriteTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void replace() {\n        doTest(i -> {\n            Entity entity = i.selectById(1);\n            assertThat(entity.getName()).isNull();\n        });\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntity.java",
    "content": "package com.baomidou.mybatisplus.test.scheam;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n */\n@Data\n@TableName(value = \"${my.tableName}\", schema = \"${my.schema}\")\npublic class SchemaEntity {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.scheam;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author nieqiurong\n */\n@Mapper\npublic interface SchemaEntityMapper extends BaseMapper<SchemaEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/scheam/SchemaEntityTest.java",
    "content": "package com.baomidou.mybatisplus.test.scheam;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.function.Consumer;\n\n/**\n * @author nieqiurong\n */\npublic class SchemaEntityTest extends BaseDbTest<SchemaEntityMapper> {\n\n    @Test\n    void test() {\n        doTest(mapper -> {\n            SchemaEntity schemaEntity = mapper.selectById(1);\n            Assertions.assertNotNull(schemaEntity);\n        });\n    }\n\n    @Override\n    protected Consumer<Configuration> consumer() {\n        return configuration -> {\n            Properties properties = new Properties();\n            properties.put(\"my.schema\", \"public\");\n            properties.put(\"my.tableName\", \"SCHEMA_ENTITY\");\n            configuration.setVariables(properties);\n        };\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into SCHEMA_ENTITY(id,name) values(1,'1'),(2,'2');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists SCHEMA_ENTITY\", \"CREATE TABLE IF NOT EXISTS SCHEMA_ENTITY (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.sqlrunner;\n\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2021-03-16\n */\n@Data\npublic class Entity {\n\n    private Long id;\n\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.sqlrunner;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2021-03-16\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/sqlrunner/SqlRunnerTest.java",
    "content": "package com.baomidou.mybatisplus.test.sqlrunner;\n\nimport com.baomidou.mybatisplus.core.injector.SqlRunnerInjector;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\n\n/**\n * @author miemie\n * @since 2021-03-16\n */\npublic class SqlRunnerTest extends BaseDbTest<EntityMapper> {\n\n    public SqlRunnerTest() {\n        SqlHelper.FACTORY = sqlSessionFactory;\n        new SqlRunnerInjector().inject(sqlSessionFactory.getConfiguration());\n    }\n\n    @Test\n    void test() {\n        assertThat(SqlRunner.db().insert(\"insert into entity(id,name) values({0},{1})\", 6, \"6\")).isTrue();\n\n        assertThat(SqlRunner.db().update(\"update entity set name = {0} where id = {1}\", \"老王\", 6)).isTrue();\n\n        assertThat(SqlRunner.db().delete(\"delete from entity where id = {0}\", 6)).isTrue();\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'1'),(2,'2'),(3,'3'),(4,'4'),(5,'5');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.strategy;\n\nimport com.baomidou.mybatisplus.annotation.FieldStrategy;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\n@Data\npublic class Entity {\n\n    private Long id;\n\n    private String name;\n\n    @TableField(insertStrategy = FieldStrategy.NOT_EMPTY)\n    private String insertStr;\n\n    @TableField(updateStrategy = FieldStrategy.NOT_EMPTY)\n    private String updateStr;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.strategy;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/strategy/StrategyTest.java",
    "content": "package com.baomidou.mybatisplus.test.strategy;\n\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\npublic class StrategyTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        long id = 1L;\n        doTestAutoCommit(i -> {\n            Entity entity = new Entity();\n            entity.setId(id);\n            entity.setName(\"entity\");\n            entity.setInsertStr(\"\");\n            i.insert(entity);\n        });\n        doTest(i -> {\n            Entity entity = i.selectById(id);\n            assertThat(entity).isNotNull();\n            assertThat(entity.getInsertStr()).isNull();\n            assertThat(entity.getUpdateStr()).isNull();\n        });\n        doTestAutoCommit(i -> {\n            Entity entity = new Entity();\n            entity.setId(id);\n            entity.setName(\"entity\");\n            entity.setUpdateStr(\"\");\n            i.updateById(entity);\n        });\n        doTest(i -> {\n            Entity entity = i.selectById(id);\n            assertThat(entity).isNotNull();\n            assertThat(entity.getInsertStr()).isNull();\n            assertThat(entity.getUpdateStr()).isNull();\n        });\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"insert_str VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"update_str VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.tenant;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\n@Data\n@Accessors(chain = true)\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6389385437936113455L;\n\n    private Long id;\n\n    private String name;\n\n    private Integer tenantId;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.tenant;\n\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport org.apache.ibatis.annotations.CacheNamespace;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\n@CacheNamespace\npublic interface EntityMapper extends BaseMapper<Entity> {\n\n    /*\n     * 请注意: Mybatis的接口方法虽然支持重载,但是底层MappedStatement是只能有一份的,也就是MappedStatement(类名+方法名)组成唯一性.\n     *\n     * 低版本(<3.5.10)下,忽略BaseMapper上的方法,可通过重写其中方法来标记\n     * 例如忽略deleteById方法,直接覆写 int deleteById(Entity entity); 这样就会把deleteById相关的重载方法都会重写掉,因为忽略方式是对MappedStatement级别生效的\n     *\n     * (高版本才支持)\n     * 但是建议按照如果有需要跳过一些插件的方法,通过自定义方法标记作用跳过会好点.\n     * 例如我调用deleteById,默认情况下是需要拼接租户条件的,但如果有些特殊需求,想忽略跳过租户的时候,可以直接自定义个默认方法(例如deleteByIdWithIgnore)来调用baseMapper方法得deleteById\n     */\n\n    @InterceptorIgnore(tenantLine = \"true\")\n    default int deleteByIdWithIgnore(Serializable id) {\n        return deleteById(id);\n    }\n\n    @InterceptorIgnore(tenantLine = \"true\")\n    default Entity selectByIdWithIgnore(Serializable id) {\n        return selectById(id);\n    }\n\n    @InterceptorIgnore(tenantLine = \"false\")\n    default Entity selectByIdWithIgnore2(Serializable id) {\n        return selectByIdWithIgnore(id);\n    }\n\n    default Entity selectByIdWithIgnore3(Serializable id) {\n        return selectByIdWithIgnore(id);\n    }\n\n    default Entity selectByIdWithIgnore4(Serializable id) {\n        return selectByIdWithIgnore2(id);\n    }\n\n    default Entity selectByIdWithIgnore5(Serializable id) {\n        return selectById(id);\n    }\n\n    default Entity selectByIdWithIgnore6(IgnoreStrategy ignoreStrategy, Serializable id) {\n        return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectById(id));\n    }\n\n    default Entity selectByIdWithIgnore7(IgnoreStrategy ignoreStrategy, Serializable id) {\n        return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectByIdWithIgnore(id));\n    }\n\n    default Entity selectByIdWithIgnore8(IgnoreStrategy ignoreStrategy, Serializable id) {\n        return InterceptorIgnoreHelper.execute(ignoreStrategy, ()-> selectByIdWithIgnore2(id));\n    }\n\n\n\n//    /**\n//     *  //TODO 由于是对ms级别的忽略,所以不考虑重载方法, 忽略deleteById方法\n//     */\n//    @InterceptorIgnore(tenantLine = \"true\")\n//    int deleteById(Entity entity);\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/tenant/TenantTest.java",
    "content": "package com.baomidou.mybatisplus.test.tenant;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport net.sf.jsqlparser.expression.LongValue;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-24\n */\npublic class TenantTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void test() {\n        Cache cache = sqlSessionFactory.getConfiguration().getCache(EntityMapper.class.getName());\n        assertThat(cache).as(\"使用 @CacheNamespace 指定了使用缓存\").isNotNull();\n\n\n        Long id = 1L;\n        doTestAutoCommit(m -> {\n            int insert = m.insert(new Entity().setId(id));\n            assertThat(insert).as(\"插入成功\").isEqualTo(1);\n        });\n\n\n        doTest(m -> {\n            Entity entity = m.selectById(id);\n            assertThat(entity).as(\"插入成功\").isNotNull();\n            assertThat(entity.getTenantId()).as(\"有租户信息\").isEqualTo(1);\n        });\n        assertThat(cache.getSize()).as(\"有一条缓存\").isEqualTo(1);\n\n\n        doTest(m -> {\n            Entity entity = m.selectById(id);\n            assertThat(entity).as(\"插入成功\").isNotNull();\n            assertThat(entity.getTenantId()).as(\"有租户信息\").isEqualTo(1);\n        });\n        assertThat(cache.getSize()).as(\"依然只有一条缓存,命中了缓存\").isEqualTo(1);\n\n\n        doTestAutoCommit(m -> {\n            int delete = m.deleteById(id);\n            assertThat(delete).as(\"删除成功\").isEqualTo(1);\n        });\n        assertThat(cache.getSize()).as(\"update操作清空了缓存\").isEqualTo(0);\n\n\n        doTestAutoCommit(m -> {\n            int insert = m.insert(new Entity().setId(id).setTenantId(2));\n            assertThat(insert).as(\"故意插入一个其他租户的信息,插入成功\").isEqualTo(1);\n        });\n\n\n        doTest(m -> {\n            Entity entity = m.selectById(id);\n            assertThat(entity).as(\"搜索不到数据\").isNull();\n        });\n        assertThat(cache.getSize()).as(\"缓存了个寂寞\").isEqualTo(1);\n\n\n        doTest(m -> {\n            Entity entity = m.selectById(id);\n            assertThat(entity).as(\"搜索不到数据\").isNull();\n        });\n        assertThat(cache.getSize()).as(\"依然缓存了个寂寞,说明命中的缓存\").isEqualTo(1);\n\n        doTest(m -> {\n            Page<Entity> page = m.selectPage(new Page<>(), null);\n            assertThat(page.getTotal()).as(\"count 正常\").isEqualTo(0);\n        });\n\n        doTest(m -> {\n            Entity entity = new Entity().setName(\"秋秋\").setTenantId(2);\n            m.insert(entity);\n            Assertions.assertNull(m.selectById(entity.getId()));\n            Assertions.assertNotNull(m.selectByIdWithIgnore(entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore2(entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore3(entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore4(entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore5(entity.getId()));\n\n            Assertions.assertNotNull(m.selectByIdWithIgnore6(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore6(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId()));\n            Assertions.assertNotNull(m.selectByIdWithIgnore7(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore7(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId()));\n            Assertions.assertNotNull(m.selectByIdWithIgnore8(IgnoreStrategy.builder().tenantLine(true).build(), entity.getId()));\n            Assertions.assertNull(m.selectByIdWithIgnore8(IgnoreStrategy.builder().tenantLine(false).build(), entity.getId()));\n\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore(entity.getId())));\n            Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore(entity.getId())));\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore2(entity.getId())));\n            Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore2(entity.getId())));\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore3(entity.getId())));\n            Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore3(entity.getId())));\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore4(entity.getId())));\n            Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore4(entity.getId())));\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(),()->m.selectByIdWithIgnore5(entity.getId())));\n            Assertions.assertNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(false).build(),()->m.selectByIdWithIgnore5(entity.getId())));\n            Assertions.assertEquals(0, m.deleteById(entity.getId()));\n            Assertions.assertEquals(1, m.deleteByIdWithIgnore(entity.getId()));\n        });\n\n        doTest(m -> {\n            Entity entity = new Entity().setName(\"秋秋\").setTenantId(2);\n            m.insert(entity);\n            Assertions.assertNull(m.selectById(entity.getId()));\n            Assertions.assertNotNull(InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> m.selectById(entity.getId())));\n            Assertions.assertEquals(0, m.deleteById(entity.getId()));\n            Assertions.assertEquals(1, InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> m.deleteById(entity.getId())));\n        });\n\n        doTest(m -> {\n            Entity entity = new Entity().setName(\"秋秋\").setTenantId(2);\n            m.insert(entity);\n            Assertions.assertNull(m.selectById(entity.getId()));\n            InterceptorIgnoreHelper.execute(IgnoreStrategy.builder().tenantLine(true).build(), () -> {\n                Assertions.assertNotNull(m.selectById(entity.getId()));\n                Assertions.assertEquals(1, m.deleteById(entity.getId()));\n            });\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(() -> new LongValue(1)));\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity values(1111,'娇妹',3)\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"tenant_id integer not NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/DbTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport com.baomidou.mybatisplus.core.spi.CompatibleSet;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.baomidou.mybatisplus.extension.toolkit.Db;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport com.baomidou.mybatisplus.test.sqlrunner.Entity;\nimport com.baomidou.mybatisplus.test.sqlrunner.EntityMapper;\nimport org.apache.ibatis.exceptions.TooManyResultsException;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.util.*;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\n/**\n * 以静态方式调用Service中的函数\n *\n * @author VampireAchao\n * @since 2022-05-03\n */\nclass DbTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void testSave() {\n        Entity entity = new Entity();\n        entity.setName(\"ruben\");\n        boolean isSuccess = Db.save(entity);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(3L, Db.count(Entity.class));\n    }\n\n    @Test\n    void testSaveBatch() {\n        List<Entity> list = Arrays.asList(new Entity(), new Entity());\n        boolean isSuccess = Db.saveBatch(list);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(4, Db.count(Entity.class));\n    }\n\n    @Test\n    void testSaveOrUpdateBatch() {\n        Entity entity = new Entity();\n        entity.setId(1L);\n        entity.setName(\"cat\");\n        List<Entity> list = Arrays.asList(new Entity(), entity);\n        boolean isSuccess = Db.saveOrUpdateBatch(list);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(3, Db.count(Entity.class));\n    }\n\n    @Test\n    void testRemoveById() {\n        Entity entity = new Entity();\n        entity.setId(1L);\n        boolean isSuccess = Db.removeById(entity);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(1, Db.count(Entity.class));\n        isSuccess = Db.removeById(2L, Entity.class);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(0, Db.count(Entity.class));\n    }\n\n    @Test\n    void testUpdateById() {\n        Entity entity = new Entity();\n        entity.setId(1L);\n        entity.setName(\"bee bee I'm a sheep\");\n        boolean isSuccess = Db.updateById(entity);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"bee bee I'm a sheep\", Db.getById(1L, Entity.class).getName());\n    }\n\n    @Test\n    void testUpdate() {\n        boolean isSuccess = Db.update(Wrappers.lambdaUpdate(Entity.class).eq(Entity::getId, 1L).set(Entity::getName, \"be better\"));\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"be better\", Db.getById(1L, Entity.class).getName());\n\n        Entity entity = new Entity();\n        entity.setId(1L);\n        entity.setName(\"bee bee I'm a sheep\");\n        isSuccess = Db.update(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L));\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"bee bee I'm a sheep\", Db.getById(1L, Entity.class).getName());\n    }\n\n    @Test\n    void testUpdateBatchById() {\n        Entity sheep = new Entity();\n        sheep.setId(1L);\n        sheep.setName(\"bee bee I'm a sheep\");\n\n        Entity ruben = new Entity();\n        ruben.setId(2L);\n        ruben.setName(\"rabbit\");\n        boolean isSuccess = Db.updateBatchById(Arrays.asList(sheep, ruben));\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"bee bee I'm a sheep\", Db.getById(1L, Entity.class).getName());\n        assertEquals(\"rabbit\", Db.getById(2L, Entity.class).getName());\n    }\n\n    @Test\n    void testRemove() {\n        boolean isSuccess = Db.remove(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L));\n        Assertions.assertTrue(isSuccess);\n        assertEquals(1, Db.count(Entity.class));\n    }\n\n    @Test\n    void testRemoveByIds() {\n        boolean isSuccess = Db.removeByIds(Arrays.asList(1L, 2L), Entity.class);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(0, Db.count(Entity.class));\n    }\n\n    @Test\n    void testRemoveByMap() {\n        boolean isSuccess = Db.removeByMap(Collections.singletonMap(\"id\", 1L), Entity.class);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(1, Db.count(Entity.class));\n    }\n\n    @Test\n    void testSaveOrUpdate() {\n        Entity entity = new Entity();\n        entity.setId(null);\n        entity.setName(\"bee bee I'm a sheep\");\n        boolean isSuccess = Db.saveOrUpdate(entity);\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"bee bee I'm a sheep\", Db.getById(entity.getId(), Entity.class).getName());\n\n        entity.setName(\"be better\");\n        isSuccess = Db.saveOrUpdate(entity, Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, entity.getId()));\n        Assertions.assertTrue(isSuccess);\n        assertEquals(\"be better\", Db.getById(entity.getId(), Entity.class).getName());\n    }\n\n    @Test\n    void testGetOne() {\n        LambdaQueryWrapper<Entity> wrapper = Wrappers.lambdaQuery(Entity.class);\n        Assertions.assertThrows(TooManyResultsException.class, () -> Db.getOne(wrapper));\n        Entity one = Db.getOne(wrapper, false);\n        Assertions.assertNotNull(one);\n        Entity entity = new Entity();\n        entity.setId(1L);\n        one = Db.getOne(entity);\n        Assertions.assertNotNull(one);\n    }\n\n    @Test\n    void testListByMap() {\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"id\", 1L);\n        List<Entity> list = Db.listByMap(map, Entity.class);\n        assertEquals(1, list.size());\n        assertEquals(\"ruben\", list.getFirst().getName());\n    }\n\n    @Test\n    void testByIds() {\n        List<Entity> list = Db.listByIds(Arrays.asList(1L, 2L), Entity.class);\n        assertEquals(2, list.size());\n    }\n\n    @Test\n    void testGetMap() {\n        Map<String, Object> map = Db.getMap(Wrappers.lambdaQuery(Entity.class));\n        Assertions.assertNotNull(map);\n\n        Entity entity = new Entity();\n        entity.setId(1L);\n        map = Db.getMap(entity);\n        Assertions.assertNotNull(map);\n    }\n\n    @Test\n    void testList() {\n        List<Entity> list = Db.list(Wrappers.lambdaQuery(Entity.class));\n        assertEquals(2, list.size());\n\n        list = Db.list(Entity.class);\n        assertEquals(2, list.size());\n\n        Entity entity = new Entity();\n        entity.setId(1L);\n        list = Db.list(entity);\n        assertEquals(1, list.size());\n    }\n\n    @Test\n    void testListMaps() {\n        List<Map<String, Object>> list = Db.listMaps(Wrappers.lambdaQuery(Entity.class));\n        assertEquals(2, list.size());\n\n        list = Db.listMaps(Entity.class);\n        assertEquals(2, list.size());\n\n        Entity entity = new Entity();\n        entity.setId(1L);\n        list = Db.listMaps(entity);\n        assertEquals(1, list.size());\n    }\n\n    @Test\n    void testListObjs() {\n        List<Entity> list = Db.listObjs(Entity.class);\n        assertEquals(2, list.size());\n\n        List<Long> objectList = Db.listObjs(Wrappers.lambdaQuery(Entity.class), Entity::getId);\n        assertEquals(2, objectList.size());\n\n        List<String> names = Db.listObjs(Entity.class, Entity::getName);\n        Assertions.assertArrayEquals(new String[]{\"ruben\", \"chocolate\"}, names.toArray());\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE));\n        return Collections.singletonList(interceptor);\n    }\n\n    @Test\n    void testPageMaps() {\n        Page<Map<String, Object>> page = Db.pageMaps(new Page<>(1, 1), Entity.class);\n        assertEquals(2, page.getTotal());\n\n        assertEquals(Db.listMaps(new Page<>(1, 1, false), Entity.class).size(), page.getRecords().size());\n\n        page = Db.pageMaps(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class));\n        assertEquals(1, page.getRecords().size());\n\n        assertEquals(Db.listMaps(new Page<>(1, 1, false), Wrappers.lambdaQuery(Entity.class)).size(), page.getRecords().size());\n    }\n\n    @Test\n    void testPage() {\n        IPage<Entity> page = Db.page(new Page<>(1, 1), Entity.class);\n        assertEquals(2, page.getTotal());\n        assertEquals(Db.list(new Page<Entity>(1, 1), Entity.class).size(), page.getRecords().size());\n\n        page = Db.page(new Page<>(1, 1), Wrappers.lambdaQuery(Entity.class));\n        assertEquals(1, page.getRecords().size());\n\n        assertEquals(Db.list(new Page<Entity>(1, 1), Wrappers.lambdaQuery(Entity.class)).size(), page.getRecords().size());\n    }\n\n    @Test\n    void testChain() {\n        QueryChainWrapper<Entity> query = Db.query(Entity.class);\n        List<Entity> list = query.eq(\"id\", 1L).list();\n        assertEquals(1, list.size());\n\n        LambdaQueryChainWrapper<Entity> lambdaQuery = Db.lambdaQuery(Entity.class);\n        list = lambdaQuery.eq(Entity::getId, 1L).list();\n        assertEquals(1, list.size());\n\n        UpdateChainWrapper<Entity> update = Db.update(Entity.class);\n        update.eq(\"id\", 1L).set(\"name\", \"bee bee I'm a sheep\").update();\n        assertEquals(\"bee bee I'm a sheep\", lambdaQuery.eq(Entity::getId, 1L).one().getName());\n\n        LambdaUpdateChainWrapper<Entity> lambdaUpdate = Db.lambdaUpdate(Entity.class);\n        lambdaUpdate.eq(Entity::getId, 1L).set(Entity::getName, \"be better\").update();\n        assertEquals(\"be better\", lambdaQuery.eq(Entity::getId, 1L).one().getName());\n    }\n\n    @Test\n    void testGetObj() {\n        String name = Db.getObj(Wrappers.lambdaQuery(Entity.class).eq(Entity::getId, 1L), Entity::getName);\n        assertEquals(\"ruben\", name);\n    }\n\n    @Test\n    void testCount() {\n        verifyCount(0, null);\n        verifyCount(0, 0L);\n        verifyCount(1, 1L);\n        verifyCount(12, 12L);\n    }\n\n    private void verifyCount(long expected, Long mockValue) {\n        EntityMapper entityMapper = mock(EntityMapper.class);\n        when(entityMapper.selectCount(any())).thenReturn(mockValue);\n        CompatibleSet compatibleSet = mock(CompatibleSet.class);\n        when(compatibleSet.getBean(EntityMapper.class)).thenReturn(entityMapper);\n        try (MockedStatic<CompatibleHelper> compatibleHelperMockedStatic = Mockito.mockStatic(CompatibleHelper.class)) {\n            compatibleHelperMockedStatic.when(CompatibleHelper::hasCompatibleSet).thenReturn(true);\n            compatibleHelperMockedStatic.when(CompatibleHelper::getCompatibleSet).thenReturn(compatibleSet);\n            assertEquals(expected, Db.count(Entity.class));\n            assertEquals(expected, Db.count(new Entity()));\n            assertEquals(expected, Db.count(Wrappers.lambdaQuery(new Entity())));\n            assertEquals(expected, Db.count(Wrappers.lambdaQuery(Entity.class)));\n        }\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'ruben'),(2,'chocolate');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/JdbcUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Jdbc 工具类测试\n *\n * @author hubin\n * @since 2020-09-15\n */\npublic class JdbcUtilsTest {\n\n    @Test\n    public void testPattern(){\n        String regex = \":dm\\\\d*:\";\n        Assertions.assertTrue(JdbcUtils.regexFind(regex, \":dm:\"));\n        Assertions.assertTrue(JdbcUtils.regexFind(regex, \":dm8:\"));\n        Assertions.assertTrue(JdbcUtils.regexFind(regex, \"123:dm6:abc\"));\n        Assertions.assertTrue(JdbcUtils.regexFind(regex, \":dm7:abc\"));\n        Assertions.assertTrue(JdbcUtils.regexFind(regex, \"a12ds:dm71:\"));\n        Assertions.assertFalse(JdbcUtils.regexFind(regex, \"a12ds:dmc1:abc\"));\n    }\n\n    @Test\n    void testGetDbType(){\n        Assertions.assertEquals(DbType.GAUSS_DB, JdbcUtils.getDbType(\"jdbc:gaussdb://127.0.0.1:8000/baomidou\"));\n        // zenith 为第三方驱动，非官方标准驱动\n        Assertions.assertEquals(DbType.GAUSS, JdbcUtils.getDbType(\"jdbc:zenith://127.0.0.1:8000/baomidou\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/SimpleQueryTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport static com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery;\nimport static java.util.stream.Collectors.*;\nimport static java.util.stream.Collectors.mapping;\nimport static org.apache.ibatis.util.MapUtil.entry;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.toolkit.SimpleQuery;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport com.baomidou.mybatisplus.test.rewrite.Entity;\nimport com.baomidou.mybatisplus.test.rewrite.EntityMapper;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.*;\n\n/**\n * 简单查询工具类测试\n *\n * @author <achao1441470436@gmail.com>\n * @since 2021/11/9 18:30\n */\nclass SimpleQueryTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    void testList() {\n        // 我要这张表里的ids\n        List<Long> entityIds = SimpleQuery.list(lambdaQuery(), Entity::getId);\n        assertThat(entityIds).containsExactly(1L, 2L);\n\n        // 可叠加后续操作\n        List<String> names = SimpleQuery.list(lambdaQuery(), Entity::getName,\n            e -> Optional.ofNullable(e.getName())\n                .map(String::toUpperCase)\n                .ifPresent(e::setName));\n        assertThat(names).containsExactly(\"RUBEN\", null);\n    }\n\n    @Test\n    void testMap() {\n        // 我要这个表里对应条件的用户，用id作为key给我一个map\n        Map<Long, Entity> idEntityMap = SimpleQuery.keyMap(\n            Wrappers.<Entity>lambdaQuery().eq(Entity::getId, 1L), Entity::getId);\n        // 校验结果\n        Entity entity = new Entity();\n        entity.setId(1L);\n        entity.setName(\"ruben\");\n        Assert.isTrue(idEntityMap.equals(Collections.singletonMap(1L, entity)), \"Ops!\");\n\n        // 如果我只想要id和name组成的map\n        Map<Long, String> idNameMap = SimpleQuery.map(lambdaQuery(), Entity::getId, Entity::getName);\n        // 校验结果\n        Map<Long, String> map = new HashMap<>(1 << 2);\n        map.put(1L, \"ruben\");\n        map.put(2L, null);\n        Assert.isTrue(idNameMap.equals(map), \"Ops!\");\n    }\n\n    @Test\n    void testGroup() {\n        // 我需要相同名字的用户的分为一组，再造一条数据\n        doTestAutoCommit(m -> {\n            Entity entity = new Entity();\n            entity.setId(3L);\n            entity.setName(\"ruben\");\n            m.insert(entity);\n        });\n\n        // 简单查询\n        Map<String, List<Entity>> nameUsersMap = SimpleQuery.group(lambdaQuery(), Entity::getName);\n\n        // 校验结果\n        Map<String, List<Entity>> map = new HashMap<>(1 << 2);\n        Entity chao = new Entity();\n        chao.setId(2L);\n        chao.setName(null);\n        map.put(null, Collections.singletonList(chao));\n\n        Entity ruben = new Entity();\n        ruben.setId(1L);\n        ruben.setName(\"ruben\");\n        Entity ruben2 = new Entity();\n        ruben2.setId(3L);\n        ruben2.setName(\"ruben\");\n        map.put(\"ruben\", Arrays.asList(ruben, ruben2));\n        Assert.isTrue(nameUsersMap.equals(map), \"Ops!\");\n\n        // 解锁高级玩法：\n        // 获取Map<name,List<id>>\n        Map<String, List<Long>> nameIdMap = SimpleQuery.group(lambdaQuery(), Entity::getName,\n            mapping(Entity::getId, toList()));\n        assertThat(nameIdMap).containsExactly(entry(null, Arrays.asList(2L)), entry(\"ruben\", Arrays.asList(1L, 3L)));\n\n        // 获取Map<name,个数>\n        Map<String, Long> nameCountMap = SimpleQuery.group(lambdaQuery(), Entity::getName, counting());\n        assertThat(nameCountMap).containsExactly(entry(null, 1L), entry(\"ruben\", 2L));\n        // ...超多花样\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1, 'ruben'), (2, null);\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n}\n\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/SqlHelperTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport com.baomidou.mybatisplus.test.rewrite.Entity;\nimport com.baomidou.mybatisplus.test.rewrite.EntityMapper;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * SqlHelper 工具类测试\n *\n * @author <achao1441470436@gmail.com>\n * @since 2020-09-15\n */\npublic class SqlHelperTest extends BaseDbTest<EntityMapper> {\n\n    @Test\n    public void testGetMapperAndExecute() {\n\n        List<Entity> entityList = SqlHelper.execute(Entity.class, m -> m.selectList(Wrappers.lambdaQuery()));\n\n        Entity ruben = new Entity();\n        ruben.setId(1L);\n        ruben.setName(\"ruben\");\n        Entity aChao = new Entity();\n        aChao.setId(2L);\n        aChao.setName(\"a chao\");\n        Assert.isTrue(entityList.equals(Arrays.asList(ruben, aChao)), \"There is something wrong,please check your environment!\");\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'ruben'),(2,'a chao');\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\", \"CREATE TABLE IF NOT EXISTS entity (\" +\n            \"id BIGINT NOT NULL,\" +\n            \"name VARCHAR(30) NULL DEFAULT NULL,\" +\n            \"PRIMARY KEY (id))\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/toolkit/StringUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 测试 StringUtils工具类测试\n *\n * @author XiaoBingBy\n * @since 2019-08-30\n */\nclass StringUtilsTest {\n\n    @Test\n    void isBlankTest() {\n        Assert.isTrue(StringUtils.isBlank(\"\"), \"error not empty\");\n\n        Assert.isTrue(StringUtils.isBlank(null), \"error not empty\");\n\n        Assert.isTrue(StringUtils.isBlank(\"   \"), \"error not empty\");\n    }\n\n    @Test\n    void sqlInjectionBlackTest(){\n        String originalStr = \"(select*from(select sleep(if(length(database())=13,3,0))union/**/select+1)a)\" ;\n        Assert.isTrue(\"(selectfrom(selectsleep(if(length(database())13,3,0))union//select1)a)\"\n            .equals(StringUtils.sqlInjectionReplaceBlank(originalStr)),\"error\");\n    }\n\n    @Test\n    void sqlInjectionBlackCharTest(){\n        Assert.isTrue(\"\".equals(StringUtils.sqlInjectionReplaceBlank(\"'\\\"<>&*+=#-;    \\n\\t\")),\"error\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/DeleteByIdDto.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class DeleteByIdDto<T> implements Serializable {\n\n    private T id;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntity.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\n\nimport java.util.UUID;\n\n@Data\n@TableName(\"entity\")\npublic class UUIDEntity {\n\n    @TableId(value = \"id\",type = IdType.INPUT)\n    private UUID id;\n\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UUIDEntityMapper extends BaseMapper<UUIDEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDEntityTest.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\npublic class UUIDEntityTest extends BaseDbTest<UUIDEntityMapper> {\n\n    @Test\n    void test() {\n        doTest(m -> {\n            var uuidEntity = new UUIDEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test1\");\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertNotNull(m.selectById(uuidEntity.getId()));\n            Assertions.assertEquals(1, m.deleteById(uuidEntity));\n        });\n\n        doTest(m -> {\n            var uuidEntity = new UUIDEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test2\");\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity.getId())));\n        });\n\n        doTest(m -> {\n            var uuidEntity = new UUIDEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test3\");\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity)));\n        });\n\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteByIds(\n            List.of(\n                UUID.randomUUID().toString(),\n                UUID.randomUUID(), 123, 321L,\n                Map.of(\"id\", UUID.randomUUID()),\n                Map.of(\"id\", UUID.randomUUID().toString()),\n                new DeleteByIdDto<>(UUID.randomUUID()),\n                new DeleteByIdDto<>(UUID.randomUUID().toString())\n            ))));\n\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID())));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID().toString())));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new UUIDEntity(){})));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID()))));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID().toString()))));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> SqlRunner.db(UUIDEntity.class).delete(\"delete from entity where id = {0}\", UUID.randomUUID())));\n    }\n\n\n    @Override\n    protected Consumer<Configuration> consumer() {\n        return configuration -> configuration.getTypeHandlerRegistry().register(UUIDTypeHandler.class);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity values('0824eb71-e124-5ba1-56b9-87185d91f309','test')\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id VARCHAR(50) NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n\n    @Override\n    protected GlobalConfig globalConfig() {\n        return super.globalConfig().setEnableSqlRunner(true);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntity.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport lombok.Data;\n\nimport java.util.UUID;\n\n@Data\n@TableName(\"entity\")\npublic class UUIDLogicEntity {\n\n    @TableId(value = \"id\",type = IdType.INPUT)\n    private UUID id;\n\n    private String name;\n\n    @TableField(fill = FieldFill.UPDATE)\n    private String deleteBy;\n\n    @TableField(fill = FieldFill.UPDATE)\n    @TableLogic(delval = \"true\", value = \"false\")\n    private Boolean deleted;\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\npublic interface UUIDLogicEntityMapper extends BaseMapper<UUIDLogicEntity> {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDLogicEntityTest.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\npublic class UUIDLogicEntityTest extends BaseDbTest<UUIDLogicEntityMapper> {\n\n    @Test\n    void test() {\n        doTest(m -> {\n            var uuidEntity = new UUIDLogicEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test1\");\n            uuidEntity.setDeleted(false);\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertNotNull(m.selectById(uuidEntity.getId()));\n            Assertions.assertEquals(1, m.deleteById(uuidEntity));\n        });\n\n        doTest(m -> {\n            var uuidEntity = new UUIDLogicEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test2\");\n            uuidEntity.setDeleted(false);\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity.getId())));\n        });\n\n        doTest(m -> {\n            var uuidEntity = new UUIDLogicEntity();\n            uuidEntity.setId(UUID.randomUUID());\n            uuidEntity.setName(\"test3\");\n            uuidEntity.setDeleted(false);\n            Assertions.assertEquals(1, m.insert(uuidEntity));\n            Assertions.assertEquals(1, m.deleteByIds(List.of(uuidEntity)));\n        });\n\n\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteByIds(\n            List.of(\n                UUID.randomUUID().toString(),\n                UUID.randomUUID(), 123, 321L,\n                Map.of(\"id\", UUID.randomUUID()),\n                Map.of(\"id\", UUID.randomUUID().toString()),\n                new DeleteByIdDto<>(UUID.randomUUID()),\n                new DeleteByIdDto<>(UUID.randomUUID().toString())\n            ))));\n\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID())));\n        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new UUIDLogicEntity(){})));\n        // TODO 下面三种类型无法转为UUID，看是否增加一个类型转换器让用户能注册处理自己的类型转换\n//        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(UUID.randomUUID().toString())));\n//        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID()))));\n//        doTest(m -> Assertions.assertDoesNotThrow(()-> m.deleteById(new DeleteByIdDto<>(UUID.randomUUID().toString()))));\n    }\n\n\n    @Override\n    protected Consumer<Configuration> consumer() {\n        return configuration -> configuration.getTypeHandlerRegistry().register(UUIDTypeHandler.class);\n    }\n\n    @Override\n    protected GlobalConfig globalConfig() {\n        return super.globalConfig().setMetaObjectHandler(new MetaObjectHandler() {\n            @Override\n            public void insertFill(MetaObject metaObject) {\n\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                metaObject.setValue(\"deleteBy\", \"baomidou\");\n            }\n        });\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity values('0824eb71-e124-5ba1-56b9-87185d91f309','test',null, false)\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id VARCHAR(50) NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"delete_by VARCHAR(30) NULL DEFAULT NULL,\" +\n                \"deleted BOOLEAN NOT NULL DEFAULT false,\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/uuid/UUIDTypeHandler.java",
    "content": "package com.baomidou.mybatisplus.test.uuid;\n\nimport org.apache.ibatis.type.*;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.UUID;\n\n@MappedTypes(UUID.class)\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class UUIDTypeHandler extends BaseTypeHandler<UUID> {\n\n    @Override\n    public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {\n        ps.setObject(i, parameter);\n    }\n\n    @Override\n    public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {\n        String uuidStr = rs.getString(columnName);\n        return uuidStr == null ? null : UUID.fromString(uuidStr);\n    }\n\n    @Override\n    public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {\n        String uuidStr = rs.getString(columnIndex);\n        return uuidStr == null ? null : UUID.fromString(uuidStr);\n    }\n\n    @Override\n    public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {\n        String uuidStr = cs.getString(columnIndex);\n        return uuidStr == null ? null : UUID.fromString(uuidStr);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/Entity.java",
    "content": "package com.baomidou.mybatisplus.test.version;\n\nimport com.baomidou.mybatisplus.annotation.Version;\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\n@Data\n@Accessors(chain = true)\npublic class Entity implements Serializable {\n    private static final long serialVersionUID = 6962439201546719734L;\n\n    private Long id;\n\n    private String name;\n\n    @Version\n    private Integer version;\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/EntityMapper.java",
    "content": "package com.baomidou.mybatisplus.test.version;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\npublic interface EntityMapper extends BaseMapper<Entity> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/version/VersionTest.java",
    "content": "package com.baomidou.mybatisplus.test.version;\n\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;\nimport com.baomidou.mybatisplus.test.BaseDbTest;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport lombok.extern.slf4j.Slf4j;\n\n/**\n * @author miemie\n * @author raylax\n * @since 2020-07-04\n */\n@Slf4j\npublic class VersionTest extends BaseDbTest<EntityMapper> {\n\n\n    @Test\n    void testWrapperMode() {\n        log.info(\"[wrapper mode] test\");\n\n        doTestAutoCommit(i -> {\n            int result = i.update(null, Wrappers.<Entity>update()\n                .eq(\"id\", 3)\n                .set(\"version\", 1)\n            );\n            assertThat(result).as(\"[wrapper mode] 设置version值成功\").isEqualTo(1);\n        });\n\n        doTestAutoCommit(i -> {\n            int result = i.update(null, Wrappers.<Entity>update()\n                .eq(\"id\", 3)\n                .eq(\"version\", 1)\n            );\n            assertThat(result).as(\"[wrapper mode] 设置version值匹配更新成功\").isEqualTo(1);\n            final Entity entity = i.selectById(3);\n            assertThat(entity.getVersion()).isEqualTo(2);\n        });\n\n        doTestAutoCommit(i -> {\n            int result = i.update(null, Wrappers.<Entity>update()\n                .eq(\"id\", 3)\n                .eq(\"version\", 1)\n            );\n            assertThat(result).as(\"[wrapper mode] 设置version值匹配更新失败\").isEqualTo(0);\n            final Entity entity = i.selectById(3);\n            assertThat(entity.getVersion()).isEqualTo(2);\n        });\n    }\n\n    @Test\n    void test() {\n        doTestAutoCommit(i -> {\n            int result = i.updateById(new Entity().setId(1L).setName(\"老张\"));\n            assertThat(result).as(\"没放入version值更新成功\").isEqualTo(1);\n        });\n\n        doTestAutoCommit(i -> {\n            int result = i.updateById(new Entity().setId(1L).setName(\"老张\").setVersion(1));\n            assertThat(result).as(\"放入的version值不匹配更新失败\").isEqualTo(0);\n        });\n\n        doTestAutoCommit(i -> {\n            Entity entity = new Entity().setId(1L).setName(\"老张\").setVersion(0);\n            int result = i.updateById(entity);\n            assertThat(result).as(\"放入的version值匹配更新成功\").isEqualTo(1);\n            assertThat(entity.getVersion()).isEqualTo(1);\n        });\n\n        doTestAutoCommit(i -> {\n            int result = i.update(new Entity().setName(\"老张\"), Wrappers.<Entity>update().eq(\"id\", 2));\n            assertThat(result).as(\"没放入version值更新成功\").isEqualTo(1);\n        });\n\n        doTestAutoCommit(i -> {\n            int result = i.update(new Entity().setName(\"老张\").setVersion(1), Wrappers.<Entity>update().eq(\"id\", 2));\n            assertThat(result).as(\"放入的version值不匹配更新失败\").isEqualTo(0);\n        });\n\n        doTestAutoCommit(i -> {\n            Entity entity = new Entity().setName(\"老张\").setVersion(0);\n            int result = i.update(entity, Wrappers.<Entity>update().eq(\"id\", 2));\n            assertThat(result).as(\"放入的version值匹配更新成功\").isEqualTo(1);\n            assertThat(entity.getVersion()).isEqualTo(1);\n        });\n    }\n\n    @Override\n    protected List<Interceptor> interceptors() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor(true));\n        return Collections.singletonList(interceptor);\n    }\n\n    @Override\n    protected String tableDataSql() {\n        return \"insert into entity(id,name) values(1,'老王'),(2,'老李'),(3,'老赵')\";\n    }\n\n    @Override\n    protected List<String> tableSql() {\n        return Arrays.asList(\"drop table if exists entity\",\n            \"CREATE TABLE IF NOT EXISTS entity (\\n\" +\n                \"id BIGINT(20) NOT NULL,\\n\" +\n                \"name VARCHAR(30) NULL DEFAULT NULL,\\n\" +\n                \"version integer NOT NULL DEFAULT 0,\\n\" +\n                \"PRIMARY KEY (id)\" +\n                \")\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/KtTestConfig.kt",
    "content": "package com.baomidou.mybatisplus.test.h2\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration\nimport com.baomidou.mybatisplus.core.config.GlobalConfig\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean\nimport com.baomidou.mybatisplus.test.h2.config.DBConfig\nimport org.apache.ibatis.session.ExecutorType\nimport org.apache.ibatis.session.SqlSessionFactory\nimport org.apache.ibatis.type.EnumOrdinalTypeHandler\nimport org.apache.ibatis.type.JdbcType\nimport org.mybatis.spring.annotation.MapperScan\nimport org.springframework.context.annotation.Bean\nimport org.springframework.context.annotation.ComponentScan\nimport org.springframework.context.annotation.Configuration\nimport org.springframework.context.annotation.Import\nimport javax.sql.DataSource\n\n/**\n * @author nieqiurong\n */\n@Configuration\n@Import(DBConfig::class)\n@ComponentScan(\"com.baomidou.mybatisplus.test.h2.kotlin\")\n@MapperScan(\"com.baomidou.mybatisplus.test.h2.kotlin.mapper\")\nopen class KtTestConfig {\n\n    @Bean(\"sqlSessionFactory\")\n    open fun sqlSessionFactory(dataSource: DataSource): SqlSessionFactory? {\n        val sqlSessionFactory = MybatisSqlSessionFactoryBean()\n        sqlSessionFactory.setDataSource(dataSource)\n        val configuration = MybatisConfiguration()\n        configuration.jdbcTypeForNull = JdbcType.NULL\n        configuration.isMapUnderscoreToCamelCase = true\n        configuration.defaultExecutorType = ExecutorType.REUSE\n        configuration.setDefaultEnumTypeHandler(EnumOrdinalTypeHandler::class.java) //默认枚举处理\n        sqlSessionFactory.configuration = configuration\n        val mybatisPlusInterceptor = MybatisPlusInterceptor()\n        mybatisPlusInterceptor.addInnerInterceptor(PaginationInnerInterceptor())\n        sqlSessionFactory.setPlugins(mybatisPlusInterceptor)\n        val globalConfig = GlobalConfig()\n        globalConfig.setMetaObjectHandler(MyMetaObjectHandler())\n        globalConfig.setSqlInjector(DefaultSqlInjector())\n        sqlSessionFactory.setGlobalConfig(globalConfig)\n        return sqlSessionFactory.getObject()\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/MetaObjectHandlerTest.kt",
    "content": "package com.baomidou.mybatisplus.test.h2\n\nimport com.baomidou.mybatisplus.annotation.FieldFill\nimport com.baomidou.mybatisplus.annotation.TableField\nimport com.baomidou.mybatisplus.core.MybatisConfiguration\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper\nimport org.apache.ibatis.builder.MapperBuilderAssistant\nimport org.apache.ibatis.reflection.MetaObject\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.Test\nimport java.math.BigDecimal\nimport java.math.BigInteger\nimport java.sql.Time\nimport java.sql.Timestamp\nimport java.time.LocalDate\nimport java.time.LocalDateTime\nimport java.util.*\n\n/**\n *\n * @author nieqiurong\n */\nclass MetaObjectHandlerTest {\n\n    @Test\n    fun println() {\n        val clzs = arrayOf(\n            String::class, Long::class, Int::class, Double::class, Float::class,\n            Short::class, Byte::class, Boolean::class, Date::class,\n            Time::class,\n            Timestamp::class, java.sql.Date::class, LocalDate::class, LocalDateTime::class, BigInteger::class,\n            BigDecimal::class, BigInteger::class\n        )\n        for (clz in clzs) {\n            println(\"kotlinType:\" + clz.simpleName + \"----->\" + \"JavaObjectType:\" + clz.javaObjectType.name + \"---->\" + \"JavaType:\" + clz.java.name)\n        }\n    }\n\n    @Test\n    fun test() {\n        val configuration = MybatisConfiguration()\n        val mapperBuilderAssistant = MapperBuilderAssistant(configuration, \"\")\n        val tableInfo = TableInfoHelper.initTableInfo(mapperBuilderAssistant, Demo::class.java)\n        for (tableFieldInfo in tableInfo.fieldList) {\n            println(tableFieldInfo.property + \"----->\" + tableFieldInfo.propertyType.name)\n        }\n        val demo = Demo()\n        val metaObjectHandler = object : MetaObjectHandler {\n            override fun insertFill(metaObject: MetaObject) {\n                this.strictInsertFill(metaObject, \"testString\", String::class.java, \"123\")\n                this.strictInsertFill(metaObject, \"testLong\", Long::class.javaObjectType, 123456L)\n                this.strictInsertFill(metaObject, \"testInt\", Int::class.javaObjectType, 123)\n                this.strictInsertFill(\n                    metaObject,\n                    \"testLocalDateTime\",\n                    LocalDateTime::class.javaObjectType,\n                    LocalDateTime.now()\n                )\n                this.strictInsertFill(metaObject, \"testBoolean\", Boolean::class.javaObjectType, false)\n                this.strictInsertFill(metaObject, \"testDate\", Date::class.javaObjectType, Date())\n                this.strictInsertFill(metaObject, \"testLocalDate\", LocalDate::class.javaObjectType, LocalDate.now())\n            }\n\n            override fun updateFill(metaObject: MetaObject) {\n\n            }\n        }\n        val metaObject: MetaObject = configuration.newMetaObject(demo)\n        metaObjectHandler.insertFill(metaObject)\n        Assertions.assertNotNull(demo.testString)\n        Assertions.assertNotNull(demo.testInt)\n        Assertions.assertNotNull(demo.testLong)\n        Assertions.assertNotNull(demo.testDate)\n        Assertions.assertNotNull(demo.testLocalDateTime)\n        Assertions.assertNotNull(demo.testBoolean)\n        Assertions.assertNotNull(demo.testLocalDate)\n        println(demo)\n    }\n\n}\n\nclass Demo {\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testString: String? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testInt: Int? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testLong: Long? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testDate: Date? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testLocalDateTime: LocalDateTime? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testBoolean: Boolean? = null\n\n    @TableField(fill = FieldFill.INSERT_UPDATE)\n    var testLocalDate: LocalDate? = null\n\n    override fun toString(): String {\n        return \"Demo(testBoolean=$testBoolean, testString=$testString, testInt=$testInt, testLong=$testLong, testDate=$testDate, testLocalDateTime=$testLocalDateTime, testLocalDate=$testLocalDate)\"\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/MyMetaObjectHandler.kt",
    "content": "package com.baomidou.mybatisplus.test.h2\n\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler\nimport org.apache.ibatis.reflection.MetaObject\nimport java.time.LocalDateTime\n\n/**\n * @author nieqiurong\n */\nclass MyMetaObjectHandler : MetaObjectHandler {\n\n    override fun insertFill(metaObject: MetaObject) {\n        this.strictInsertFill(metaObject, \"createdDt\", LocalDateTime::class.javaObjectType, LocalDateTime.now())\n    }\n\n    override fun updateFill(metaObject: MetaObject) {\n        this.strictUpdateFill(metaObject, \"lastUpdatedDt\", LocalDateTime::class.javaObjectType, LocalDateTime.now())\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/KtH2UserTest.kt",
    "content": "package com.baomidou.mybatisplus.test.h2.kotlin\n\nimport com.baomidou.mybatisplus.test.h2.KtTestConfig\nimport com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum\nimport com.baomidou.mybatisplus.test.h2.kotlin.service.KtH2UserService\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.test.context.ContextConfiguration\nimport org.springframework.test.context.junit.jupiter.SpringExtension\n\n/**\n * Kotlin h2user test\n *\n * @author FlyInWind\n * @since 2020/10/18\n */\n@ExtendWith(SpringExtension::class)\n@ContextConfiguration(classes = [KtTestConfig::class])\nclass KtH2UserTest {\n\n    @Autowired\n    private lateinit var userService: KtH2UserService\n\n\n    @Test\n    fun testSave() {\n        val user = KtH2User()\n        user.age = AgeEnum.ONE\n        user.name = \"Demo\"\n        userService.save(user)\n        Assertions.assertNotNull(user.createdDt)\n    }\n\n    @Test\n    fun testUpdate() {\n        val user = KtH2User()\n        user.age = AgeEnum.ONE\n        user.name = \"Demo\"\n        userService.save(user)\n        user.name = \"Update\"\n        userService.updateById(user)\n        Assertions.assertNotNull(user.lastUpdatedDt)\n    }\n\n    @Test\n    fun testDelete() {\n        val user = KtH2User()\n        user.age = AgeEnum.ONE\n        user.name = \"Delete\"\n        userService.save(user)\n        userService.removeById(user)\n        Assertions.assertNull(userService.getById(user.testId))\n    }\n\n\n    @Test\n    fun testServiceImplInnerKtChain() {\n        var tomcat = userService.ktQuery().eq(KtH2User::name, \"Tomcat\").one()\n        Assertions.assertNotNull(tomcat)\n        Assertions.assertNotEquals(0, userService.ktQuery().like(KtH2User::name, \"a\").count())\n\n        val users = userService.ktQuery()\n            .like(KtH2User::age, AgeEnum.TWO)\n            .ne(KtH2User::version, 1)\n            .isNull(KtH2User::price)\n            .list()\n        Assertions.assertTrue(users.isEmpty())\n\n\n        userService.ktUpdate()\n            .set(KtH2User::name, \"Tomcat2\")\n            .eq(KtH2User::name, \"Tomcat\")\n            .update()\n        tomcat = userService.ktQuery().eq(KtH2User::name, \"Tomcat\").one()\n        Assertions.assertNull(tomcat)\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/entity/KtH2User.kt",
    "content": "package com.baomidou.mybatisplus.test.h2.kotlin.entity\n\nimport com.baomidou.mybatisplus.annotation.*\nimport com.baomidou.mybatisplus.test.h2.enums.AgeEnum\nimport java.math.BigDecimal\nimport java.time.LocalDateTime\nimport java.util.*\n\n@TableName(\"h2user\")\nclass KtH2User {\n\n    @TableId\n    var testId: Long? = null\n\n    var name: String? = null\n\n    var age: AgeEnum? = null\n\n    var price: BigDecimal? = null\n\n    var testType: Int? = null\n\n    @TableField(\"`desc`\")\n    var desc: String? = null\n\n    @TableField(select = false)\n    var testDate: Date? = null\n\n    @Version\n    var version: Int? = null\n\n    @TableLogic\n    val deleted: Int? = null\n\n    @TableField(fill = FieldFill.INSERT)\n    var createdDt: LocalDateTime? = null\n\n    @TableField(fill = FieldFill.UPDATE)\n    var lastUpdatedDt: LocalDateTime? = null\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/mapper/KtUserMapper.kt",
    "content": "package com.baomidou.mybatisplus.test.h2.kotlin.mapper\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper\nimport com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User\nimport org.apache.ibatis.annotations.Mapper\n\n@Mapper\ninterface KtUserMapper : BaseMapper<KtH2User> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/service/KtH2UserService.kt",
    "content": "package com.baomidou.mybatisplus.test.h2.kotlin.service\n\nimport com.baomidou.mybatisplus.extension.service.IService\nimport com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User\n\ninterface KtH2UserService : IService<KtH2User> {\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/kotlin/com/baomidou/mybatisplus/test/h2/kotlin/service/impl/KtH2UserServiceImpl.kt",
    "content": "package com.baomidou.mybatisplus.test.h2.kotlin.service.impl\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl\nimport com.baomidou.mybatisplus.test.h2.kotlin.entity.KtH2User\nimport com.baomidou.mybatisplus.test.h2.kotlin.mapper.KtUserMapper\nimport com.baomidou.mybatisplus.test.h2.kotlin.service.KtH2UserService\nimport org.springframework.stereotype.Service\n\n@Service\nclass KtH2UserServiceImpl : ServiceImpl<KtUserMapper, KtH2User>(), KtH2UserService {\n\n}\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/cache/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  t_cache (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\ninsert into t_cache values (1,'a');\ninsert into t_cache values (2,'b');\ninsert into t_cache values (3,'c');\ninsert into t_cache values (4,'d');\ninsert into t_cache values (5,'e');\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/cache/package-info.java",
    "content": "/**\n * @author miemie\n * @since 2022-03-07\n */\npackage com.baomidou.mybatisplus.test.cache;\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/cache/xml/XmlCacheMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.cache.xml.XmlCacheMapper\">\n    <cache/>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/enums/EntityMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.enums.EntityMapper\">\n\n    <resultMap id=\"result\" type=\"com.baomidou.mybatisplus.test.enums.Entity\" autoMapping=\"true\">\n        <result property=\"enumOrdinal\" column=\"enum_ordinal\"\n                typeHandler=\"org.apache.ibatis.type.EnumOrdinalTypeHandler\"/>\n    </resultMap>\n\n    <select id=\"findById\" resultMap=\"result\">\n        select *\n        from entity\n        where id = #{id}\n    </select>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/fill/FillMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.fill.FillMapper\">\n\n    <insert id=\"insertBatch1\">\n        insert into t_fill(id, name) values\n        <foreach collection=\"list\" item=\"item\" separator=\",\">\n            (#{item.id}, #{item.name})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertBatch2\">\n        insert into t_fill(id, name) values\n        <foreach collection=\"mpList\" item=\"item\" separator=\",\">\n            (#{item.id}, #{item.name})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertBatch3\">\n        insert into t_fill(id, name) values\n        <foreach collection=\"mybatisList\" item=\"item\" separator=\",\">\n            (#{item.id}, #{item.name})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertBatch4\">\n        insert into t_fill(id, name) values\n        <foreach collection=\"list\" item=\"item\" separator=\",\">\n            (#{item.id}, #{item.name})\n        </foreach>\n    </insert>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/h2/mapper/H2StudentMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.h2.mapper.H2StudentMapper\">\n\n    <insert id=\"insertFillByCustomMethod1\">\n        insert into h2user(test_id, name, age, test_type)\n        values (#{testId}, #{name}, #{age}, #{testType})\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod2\">\n        insert into h2user(test_id, name, age, test_type)\n        values (#{et.testId}, #{et.name}, #{et.age}, #{et.testType})\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod3\">\n        insert into h2user(test_id, name, age, test_type)\n        values (#{et.testId}, #{test}, #{et.age}, #{et.testType})\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod4\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"collection\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod5\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"collection\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod6\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"coll\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod7\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"list\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod8\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"array\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod9\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"array\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod10\">\n        insert into h2user(test_id, name, age, test_type)\n        values (#{et.testId}, #{et.name}, #{et.age}, #{et.testType})\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod11\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"list\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n\n    <insert id=\"insertFillByCustomMethod12\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"coll\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <insert id=\"insertFillByCustomMethod13\">\n        insert into h2user(test_id, name, age, test_type) values\n        <foreach collection=\"array\" item=\"col\" separator=\",\">\n            (#{col.testId}, #{col.name}, #{col.age}, #{col.testType})\n        </foreach>\n    </insert>\n\n    <update id=\"updateFillByCustomMethod1\">\n        update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in (\n        <foreach collection=\"list\" item=\"col\" separator=\",\">\n            #{col}\n        </foreach>\n        )\n    </update>\n\n    <update id=\"updateFillByCustomMethod2\">\n        update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in (\n        <foreach collection=\"coll\" item=\"col\" separator=\",\">\n            #{col}\n        </foreach>\n        )\n    </update>\n\n    <update id=\"updateFillByCustomMethod3\">\n        update h2user set deleted = 1,last_updated_dt = #{user.lastUpdatedDt} where test_id in (\n        <foreach collection=\"coll\" item=\"col\" separator=\",\">\n            #{col}\n        </foreach>\n        )\n    </update>\n\n    <update id=\"updateFillByCustomMethod4\">\n        update h2user set deleted = 1,last_updated_dt = #{et.lastUpdatedDt} where test_id in (\n        <foreach collection=\"colls\" item=\"col\" separator=\",\">\n            #{col}\n        </foreach>\n        )\n    </update>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/puginsome/AMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.puginsome.AMapper\">\n\n    <resultMap id=\"xxp\" type=\"com.baomidou.mybatisplus.test.puginsome.A\" autoMapping=\"true\">\n        <association property=\"b\" column=\"id\" javaType=\"com.baomidou.mybatisplus.test.puginsome.B\"\n                     select=\"com.baomidou.mybatisplus.test.puginsome.BMapper.selectById\"/>\n    </resultMap>\n\n    <select id=\"list\" resultMap=\"xxp\">\n        select *\n        from a\n    </select>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/puginsome/BMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.puginsome.BMapper\">\n\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/resultmap/EntityMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.resultmap.EntityMapper\">\n\n    <resultMap id=\"baseResult\" type=\"com.baomidou.mybatisplus.test.resultmap.Entity\">\n        <id column=\"id\" property=\"id\"/>\n        <result column=\"gg1\" property=\"gg1\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/>\n    </resultMap>\n\n    <resultMap id=\"resultMap\" type=\"com.baomidou.mybatisplus.test.resultmap.Entity\" extends=\"baseResult\">\n        <result column=\"gg2\" property=\"gg2\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/>\n        <result property=\"gg3\" column=\"gg3\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/>\n        <result property=\"gg4\" column=\"gg4\" javaType=\"list\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/>\n        <result property=\"str\" column=\"str\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/>\n    </resultMap>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/com/baomidou/mybatisplus/test/rewrite/EntityMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.rewrite.EntityMapper\">\n\n    <select id=\"selectById\" resultType=\"com.baomidou.mybatisplus.test.rewrite.Entity\">\n        select id\n        from entity\n        where id = #{id}\n    </select>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/customfilltest/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  t_fill_test (\n\tid BIGINT(20) NOT NULL,\n\ta VARCHAR(30) NULL DEFAULT NULL ,\n\tb VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/fillperformance/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  t_fill_performance (\n\tid BIGINT(20) NOT NULL,\n\ta VARCHAR(30) NULL DEFAULT NULL ,\n\tb VARCHAR(30) NULL DEFAULT NULL ,\n\tc VARCHAR(30) NULL DEFAULT NULL ,\n\td VARCHAR(30) NULL DEFAULT NULL ,\n\te VARCHAR(30) NULL DEFAULT NULL ,\n\tf VARCHAR(30) NULL DEFAULT NULL ,\n\tg VARCHAR(30) NULL DEFAULT NULL ,\n\th VARCHAR(30) NULL DEFAULT NULL ,\n\ti VARCHAR(30) NULL DEFAULT NULL ,\n\tj VARCHAR(30) NULL DEFAULT NULL ,\n\tk VARCHAR(30) NULL DEFAULT NULL ,\n\tl VARCHAR(30) NULL DEFAULT NULL ,\n\tm VARCHAR(30) NULL DEFAULT NULL ,\n\tn VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-cache-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.cache\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.cache.CacheConfig\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/cache/*.sql\"/>\n    </bean>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-custom-fill-test-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.customfill\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/customfilltest/*.sql\"/>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-fill-performance-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.fillperformance\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/fillperformance/*.sql\"/>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-id-generator-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.idgenerator\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/idgenerator/*.sql\"/>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-keygenerator-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.keygenerator\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/keygenerator/*.sql\"/>\n    </bean>\n    <bean class=\"com.baomidou.mybatisplus.test.h2.keygenerator.KeyGeneratorConfig\"/>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-logic-delete-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.service\"/>\n\n    <bean name=\"/DBConfig\" class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\"/>\n    <bean name=\"/MybatisPlusConfig\" class=\"com.baomidou.mybatisplus.test.h2.config.MybatisPlusConfigLogicDelete\"/>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-sharding-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.sharding\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.sharding.ShardingConfig\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/sharding/*.sql\"/>\n    </bean>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-tenant-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.tenant\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.tenant.TenantConfig\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/tenant/*.sql\"/>\n    </bean>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-test-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.service\"/>\n\n    <bean name=\"/DBConfig\" class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\"/>\n    <bean name=\"/MybatisPlusConfig\" class=\"com.baomidou.mybatisplus.test.h2.config.MybatisPlusConfig\"/>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/spring-test-xml-h2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.service\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\"/>\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.MybatisXmlConfig\"/>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/student.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  h2student (\n\tid BIGINT(20) NOT NULL AUTO_INCREMENT,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tgrade INT(3) NULL,\n\tgender VARCHAR(10) NULL,\n\tage INT(11) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/student.insert.sql",
    "content": "delete from h2student;\ninsert into h2student (id, name, age)values (1, 'Tom', 1);\ninsert into h2student (id, name, age)values (2, 'Jerry', 1);\ninsert into h2student (id, name, age)values (12, '要开除的学生', 1);\ninsert into h2student (id, name, age)values (13, 'test1', 1);\ninsert into h2student (id, name, age)values (14, 'test2', 1);\ninsert into h2student (id, name, age)values (15, 'test3', 1);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/user.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  h2user (\n\ttest_id BIGINT(20) NOT NULL AUTO_INCREMENT,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tage INT(11) NULL DEFAULT NULL ,\n\ttest_type INT(11) NULL ,\n\ttest_date DATETIME NULL DEFAULT NULL,\n\tprice DECIMAL(10,2) NULL DEFAULT NULL,\n\tdesc VARCHAR(30) NULL DEFAULT NULL ,\n\tversion INT(5) NULL DEFAULT NULL,\n\tcreated_dt TIMESTAMP NULL,\n\tlast_updated_dt TIMESTAMP NULL,\n\tdeleted INT(1) NULL DEFAULT 0 ,\n\tPRIMARY KEY (test_id)\n);\nCOMMENT ON COLUMN h2user.test_id IS 'PK';\nCOMMENT ON COLUMN h2user.name IS 'USERNAME';\n\n\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/h2/user.insert.sql",
    "content": "delete from h2user;\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (101, 'Tomcat', '2017-1-1 1:1:1', 3, 9.99, 1, 1, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (102, 'Jerry', '2017-3-1 1:1:1', 2, 19.99, 2, 2, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (103, 'Bob', '2017-4-1 1:1:1', 1, 99.99, 3, 3, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (104, 'Joe', '2017-2-1 1:1:1', 1, 1.99, 1, 4, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (105, 'Tony', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1010, '1010', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1011, '1011', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0);\ninsert into h2user (test_id, name, test_date, age, price, test_type, version, deleted)values (1012, '1012', '2017-2-1 1:1:1', 3, 1.99, 1, 5, 0);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/hbase-site.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--Autogenerated by Cloudera Manager-->\n<configuration>\n  <property>\n    <name>phoenix.schema.isNamespaceMappingEnabled</name>\n    <value>true</value>\n  </property>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/idgenerator/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  t_id_generator_long (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_bigdecimal (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_biginteger (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_long_string (\n\tid varchar NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_int (\n\tid int NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_int_string (\n\tid varchar NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_id_generator_string (\n\tid varchar (50) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/issues/genericid/spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.h2.issues.genericid\"/>\n\n    <bean class=\"com.baomidou.mybatisplus.test.h2.config.DBConfig\">\n        <property name=\"locationPattern\" value=\"classpath:/issues/genericid/sql/*.sql\"/>\n    </bean>\n\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/issues/genericid/sql/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  t_i171cq_long (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE TABLE IF NOT EXISTS  t_i171cq_string (\n\tid varchar NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/keygenerator/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  key_generator_model (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\tPRIMARY KEY (id)\n);\n\nCREATE SEQUENCE key_generator_model_seq START WITH 1 INCREMENT BY 1;\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoder 默认配置为PatternLayoutEncoder -->\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"com.baomidou.mybatisplus\" level=\"debug\" additivity=\"false\">\n        <appender-ref ref=\"STDOUT\"/>\n    </logger>\n\n    <root level=\"debug\">\n        <appender-ref ref=\"STDOUT\"/>\n    </root>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/mybatis-config-object-factory.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n        <setting name=\"defaultEnumTypeHandler\" value=\"org.apache.ibatis.type.EnumOrdinalTypeHandler\"/>\n    </settings>\n\n    <objectFactory type=\"com.baomidou.mybatisplus.test.reflection.ExampleObjectFactory\">\n        <property name=\"someProperty\" value=\"100\"/>\n    </objectFactory>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n    <settings>\n        <setting name=\"defaultEnumTypeHandler\"\n                 value=\"com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler\"/>\n    </settings>\n    <environments default=\"test\">\n        <environment id=\"test\">\n            <transactionManager type=\"JDBC\"/>\n            <dataSource type=\"POOLED\">\n                <property name=\"driver\" value=\"org.h2.Driver\" />\n                <property name=\"url\" value=\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\"/>\n                <property name=\"username\" value=\"sa\"/>\n                <property name=\"password\" value=\"\"/>\n            </dataSource>\n        </environment>\n    </environments>\n\n    <mappers>\n        <mapper class=\"com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper\"/>\n    </mappers>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/phoenix/spring-test-phoenix.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.phoenix.config\"/>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/phoenix/test_info.ddl.sql",
    "content": "-- DROP SCHEMA TEST;\n-- CREATE SCHEMA TEST;\nUSE TEST;\n\nDROP TABLE IF EXISTS TEST_INFO;\nCREATE TABLE TEST_INFO (\n  id INTEGER NOT NULL PRIMARY KEY,\n  name VARCHAR,\n  phone VARCHAR,\n  position VARCHAR,\n  department VARCHAR,\n  company VARCHAR,\n  file_name VARCHAR,\n  pos_dep_com VARCHAR,\n  gmt_updated DATE,\n  gmt_create DATE\n);\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/postgresql/spring-test-postgresql.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:context=\"http://www.springframework.org/schema/context\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\n       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.postgresql.service\"/>\n    <context:component-scan base-package=\"com.baomidou.mybatisplus.test.postgresql.config\"/>\n</beans>\n"
  },
  {
    "path": "mybatis-plus/src/test/resources/tenant/init.ddl.sql",
    "content": "CREATE TABLE IF NOT EXISTS  student (\n\tid BIGINT(20) NOT NULL,\n\tname VARCHAR(30) NULL DEFAULT NULL ,\n\ttenant_id BIGINT(20) NOT NULL ,\n\tPRIMARY KEY (id)\n);\ninsert into student values (1,'a',1);\ninsert into student values (2,'b',2);\n\n"
  },
  {
    "path": "mybatis-plus-annotation/build.gradle",
    "content": "dependencies {\n    implementation \"${lib.mybatis}\"\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/DbType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * MybatisPlus 支持的数据库类型,主要用于分页方言\n *\n * @author hubin\n * @since 2018-06-23\n */\n@Getter\n@AllArgsConstructor\npublic enum DbType {\n\n    /**\n     * MYSQL\n     */\n    MYSQL(\"mysql\", \"MySql数据库\"),\n    /**\n     * MARIADB\n     */\n    MARIADB(\"mariadb\", \"MariaDB数据库\"),\n    /**\n     * ORACLE\n     */\n    ORACLE(\"oracle\", \"Oracle11g及以下数据库(高版本推荐使用ORACLE_NEW)\"),\n    /**\n     * oracle12c new pagination\n     */\n    ORACLE_12C(\"oracle12c\", \"Oracle12c+数据库\"),\n    /**\n     * DB2\n     */\n    DB2(\"db2\", \"DB2数据库\"),\n    /**\n     * H2\n     */\n    H2(\"h2\", \"H2数据库\"),\n    /**\n     * HSQL\n     */\n    HSQL(\"hsql\", \"HSQL数据库\"),\n    /**\n     * SQLITE\n     */\n    SQLITE(\"sqlite\", \"SQLite数据库\"),\n    /**\n     * POSTGRE\n     */\n    POSTGRE_SQL(\"postgresql\", \"Postgre数据库\"),\n    /**\n     * SQLSERVER2005\n     */\n    SQL_SERVER2005(\"sqlserver2005\", \"SQLServer2005数据库\"),\n    /**\n     * SQLSERVER\n     */\n    SQL_SERVER(\"sqlserver\", \"SQLServer数据库\"),\n    /**\n     * DM\n     */\n    DM(\"dm\", \"达梦数据库\"),\n    /**\n     * xugu\n     */\n    XU_GU(\"xugu\", \"虚谷数据库\"),\n    /**\n     * Kingbase\n     */\n    KINGBASE_ES(\"kingbasees\", \"人大金仓数据库\"),\n    /**\n     * Phoenix\n     */\n    PHOENIX(\"phoenix\", \"Phoenix HBase数据库\"),\n    /**\n     * Gauss\n     * <p>\n     * 低版本为 zenith，为贡献者提供，非标准官方驱动，3.5.11 修改成 gauss\n     * </p>\n     *\n     * @see #GAUSS_DB\n     * @deprecated 3.5.13 使用官方标准名称\n     */\n    @Deprecated\n    GAUSS(\"gauss\", \"Gauss 数据库\"),\n    /**\n     * GaussDB\n     *\n     * @since 3.5.13\n     */\n    GAUSS_DB(\"gaussDB\", \"GaussDB 数据库\"),\n    /**\n     * ClickHouse\n     */\n    CLICK_HOUSE(\"clickhouse\", \"clickhouse 数据库\"),\n    /**\n     * GBase\n     */\n    GBASE(\"gbase\", \"南大通用(华库)数据库\"),\n    /**\n     * GBase-8s\n     */\n    GBASE_8S(\"gbase-8s\", \"南大通用数据库 GBase 8s\"),\n    /**\n     * use {@link  #GBASE_8S}\n     *\n     * @deprecated 2022-05-30\n     */\n    @Deprecated\n    GBASEDBT(\"gbasedbt\", \"南大通用数据库\"),\n    /**\n     * use {@link  #GBASE_8S}\n     *\n     * @deprecated 2022-05-30\n     */\n    @Deprecated\n    GBASE_INFORMIX(\"gbase 8s\", \"南大通用数据库 GBase 8s\"),\n    /**\n     * GBase8sPG\n     */\n    GBASE8S_PG(\"gbase8s-pg\", \"南大通用数据库 GBase 8s兼容pg\"),\n    /**\n     * GBase8c\n     */\n    GBASE_8C(\"gbase8c\", \"南大通用数据库 GBase 8c\"),\n    /**\n     * Sinodb\n     */\n    SINODB(\"sinodb\", \"星瑞格数据库\"),\n    /**\n     * Oscar\n     */\n    OSCAR(\"oscar\", \"神通数据库\"),\n    /**\n     * Sybase\n     */\n    SYBASE(\"sybase\", \"Sybase ASE 数据库\"),\n    /**\n     * OceanBase\n     */\n    OCEAN_BASE(\"oceanbase\", \"OceanBase 数据库\"),\n    /**\n     * Firebird\n     */\n    FIREBIRD(\"Firebird\", \"Firebird 数据库\"),\n    /**\n     * HighGo\n     */\n    HIGH_GO(\"highgo\", \"瀚高数据库\"),\n    /**\n     * CUBRID\n     */\n    CUBRID(\"cubrid\", \"CUBRID数据库\"),\n    /**\n     * SUNDB\n     */\n    SUNDB(\"sundb\", \"SUNDB数据库\"),\n    /**\n     * Hana\n     */\n    SAP_HANA(\"hana\", \"SAP_HANA数据库\"),\n    /**\n     * Impala\n     */\n    IMPALA(\"impala\", \"impala数据库\"),\n    /**\n     * Vertica\n     */\n    VERTICA(\"vertica\", \"vertica数据库\"),\n    /**\n     * xcloud\n     */\n    XCloud(\"xcloud\", \"行云数据库\"),\n    /**\n     * redshift\n     */\n    REDSHIFT(\"redshift\", \"亚马逊redshift数据库\"),\n    /**\n     * openGauss\n     */\n    OPENGAUSS(\"openGauss\", \"华为 opengauss 数据库\"),\n    /**\n     * TDengine\n     */\n    TDENGINE(\"TDengine\", \"TDengine数据库\"),\n    /**\n     * Informix\n     */\n    INFORMIX(\"informix\", \"Informix数据库\"),\n    /**\n     * uxdb\n     */\n    UXDB(\"uxdb\", \"优炫数据库\"),\n    /**\n     * lealone\n     */\n    LEALONE(\"lealone\", \"Lealone数据库\"),\n    /**\n     * trino\n     */\n    TRINO(\"trino\", \"Trino数据库\"),\n    /**\n     * presto\n     */\n    PRESTO(\"presto\", \"Presto数据库\"),\n    /**\n     * derby\n     */\n    DERBY(\"derby\", \"Derby数据库\"),\n    /**\n     * vastbase\n     */\n    VASTBASE(\"vastbase\", \"Vastbase数据库\"),\n    /**\n     * goldendb\n     */\n    GOLDENDB(\"goldendb\", \"GoldenDB数据库\"),\n    /**\n     * duckdb\n     */\n    DUCKDB(\"duckdb\", \"duckdb数据库\"),\n    /**\n     * yasdb\n     */\n    YASDB(\"yasdb\", \"崖山数据库\"),\n    /**\n     * Hadoop的数据仓库\n     */\n    HIVE2(\"hive2\", \"Hadoop数据仓库\"),\n    /**\n     * UNKNOWN DB\n     */\n    OTHER(\"other\", \"其他数据库\");\n\n    /**\n     * 数据库名称\n     */\n    private final String db;\n    /**\n     * 描述\n     */\n    private final String desc;\n\n    /**\n     * 获取数据库类型\n     *\n     * @param dbType 数据库类型字符串\n     */\n    public static DbType getDbType(String dbType) {\n        for (DbType type : DbType.values()) {\n            if (type.db.equalsIgnoreCase(dbType)) {\n                return type;\n            }\n        }\n        return OTHER;\n    }\n\n    public boolean mysqlSameType() {\n        return this == DbType.MYSQL\n            || this == DbType.MARIADB\n            || this == DbType.GBASE\n            || this == DbType.OSCAR\n            || this == DbType.XU_GU\n            || this == DbType.CLICK_HOUSE\n            || this == DbType.OCEAN_BASE\n            || this == DbType.CUBRID\n            || this == DbType.SUNDB\n            || this == DbType.GOLDENDB\n            || this == DbType.YASDB;\n    }\n\n    public boolean oracleSameType() {\n        return this == DbType.ORACLE\n            || this == DbType.DM\n            || this == DbType.GAUSS;\n    }\n\n    public boolean postgresqlSameType() {\n        return this == DbType.POSTGRE_SQL\n            || this == DbType.H2\n            || this == DbType.LEALONE\n            || this == DbType.SQLITE\n            || this == DbType.HSQL\n            || this == DbType.KINGBASE_ES\n            || this == DbType.PHOENIX\n            || this == DbType.SAP_HANA\n            || this == DbType.IMPALA\n            || this == DbType.HIGH_GO\n            || this == DbType.VERTICA\n            || this == DbType.REDSHIFT\n            || this == DbType.OPENGAUSS\n            || this == DbType.TDENGINE\n            || this == DbType.UXDB\n            || this == DbType.GBASE8S_PG\n            || this == DbType.GBASE_8C\n            || this == DbType.VASTBASE\n            || this == DbType.DUCKDB;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/EnumValue.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 支持普通枚举类字段, 只用在enum类的字段上\n * <p>当实体类的属性是普通枚举，且是其中一个字段，使用该注解来标注枚举类里的那个属性对应字段</p>\n * <p>\n * 使用方式参考 com.baomidou.mybatisplus.test.h2.H2StudentMapperTest\n * <pre>\n * &#64;TableName(\"student\")\n * class Student {\n *     private Integer id;\n *     private String name;\n *     private GradeEnum grade;//数据库grade字段类型为int\n * }\n *\n * public enum GradeEnum {\n *     PRIMARY(1,\"小学\"),\n *     SECONDORY(\"2\", \"中学\"),\n *     HIGH(3, \"高中\");\n *\n *     &#64;EnumValue\n *     private final int code;\n *     private final String descp;\n * }\n * </pre>\n * </p>\n *\n * @author yuxiaobin\n * @date 2018/8/30\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface EnumValue {\n\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldFill.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\n/**\n * 字段填充策略枚举类\n *\n * <p>\n * 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成\n * <if test=\"...\">......</if>\n * 判断优先级比 {@link FieldStrategy} 高\n * </p>\n *\n * @author hubin\n * @since 2017-06-27\n */\npublic enum FieldFill {\n    /**\n     * 默认不处理\n     */\n    DEFAULT,\n    /**\n     * 插入时填充字段\n     */\n    INSERT,\n    /**\n     * 更新时填充字段\n     */\n    UPDATE,\n    /**\n     * 插入和更新时填充字段\n     */\n    INSERT_UPDATE\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/FieldStrategy.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\n/**\n * 字段策略枚举类\n * <p>\n * 如果字段是基本数据类型则最终效果等同于 {@link #ALWAYS}\n *\n * @author hubin\n * @since 2016-09-09\n */\npublic enum FieldStrategy {\n    /**\n     * 任何时候都加入 SQL\n     */\n    ALWAYS,\n    /**\n     * 非NULL判断\n     */\n    NOT_NULL,\n    /**\n     * 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)\n     */\n    NOT_EMPTY,\n    /**\n     * 默认的,一般只用于注解里\n     * <p>1. 在全局里代表 NOT_NULL</p>\n     * <p>2. 在注解里代表 跟随全局</p>\n     */\n    DEFAULT,\n    /**\n     * 不加入 SQL\n     */\n    NEVER\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/IEnum.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.io.Serializable;\n\n/**\n * 自定义枚举接口\n *\n * @author hubin\n * @since 3.4.0\n */\npublic interface IEnum<T extends Serializable> {\n\n    /**\n     * 枚举数据库存储值\n     */\n    T getValue();\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/IdType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport lombok.Getter;\n\n/**\n * 生成ID类型枚举类\n *\n * @author hubin\n * @since 2015-11-10\n */\n@Getter\npublic enum IdType {\n    /**\n     * 数据库ID自增\n     * <p>该类型请确保数据库设置了 ID自增 否则无效</p>\n     */\n    AUTO(0),\n    /**\n     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)\n     */\n    NONE(1),\n    /**\n     * 用户输入ID\n     * <p>该类型可以通过自己注册自动填充插件进行填充</p>\n     */\n    INPUT(2),\n\n    /* 以下2种类型、只有当插入对象ID 为空，才自动填充。 */\n    /**\n     * 分配ID (主键类型为number或string）,\n     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)\n     *\n     * @since 3.3.0\n     */\n    ASSIGN_ID(3),\n    /**\n     * 分配UUID (主键类型为 string)\n     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace(\"-\",\"\"))\n     */\n    ASSIGN_UUID(4);\n\n    private final int key;\n\n    IdType(int key) {\n        this.key = key;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/InterceptorIgnore.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 内置插件的一些过滤规则\n * <p>\n * 支持注解在 Mapper 上以及 Mapper.Method 上\n * 同时存在则 Mapper.method 比 Mapper 优先级高\n * <p>\n * 支持:\n * true 和 false , 1 和 0 , on 和 off\n * <p>\n * 各属性返回 true 表示不走插件(在配置了插件的情况下,不填则默认表示 false)\n *\n * @author miemie\n * @since 2020-07-31\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.METHOD})\npublic @interface InterceptorIgnore {\n\n    /**\n     * 行级租户 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor}\n     */\n    String tenantLine() default \"\";\n\n    /**\n     * 动态表名 {@link com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor}\n     */\n    String dynamicTableName() default \"\";\n\n    /**\n     * 攻击 SQL 阻断解析器,防止全表更新与删除 {@link com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor}\n     */\n    String blockAttack() default \"\";\n\n    /**\n     * 垃圾SQL拦截 {@link com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor}\n     */\n    String illegalSql() default \"\";\n\n    /**\n     * 数据权限 {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor}\n     * <p>\n     * 默认关闭，需要注解打开\n     */\n    String dataPermission() default \"1\";\n\n    /**\n     * 其他的\n     * <p>\n     * 格式应该为:  \"key\"+\"@\"+可选项[false,true,1,0,on,off]\n     * 例如: \"xxx@1\" 或 \"xxx@true\" 或 \"xxx@on\"\n     * <p>\n     * 如果配置了该属性的注解是注解在 Mapper 上的,则如果该 Mapper 的一部分 Method 需要取反则需要在 Method 上注解并配置此属性为反值\n     * 例如: \"xxx@1\" 在 Mapper 上, 则 Method 上需要 \"xxx@0\"\n     */\n    String[] others() default {};\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/KeySequence.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 序列主键策略\n * <p>oracle</p>\n *\n * @author zashitou\n * @since 2017.4.20\n */\n@Documented\n@Inherited\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})\npublic @interface KeySequence {\n\n    /**\n     * 序列名\n     */\n    String value() default \"\";\n\n    /**\n     * 数据库类型，未配置默认使用注入 IKeyGenerator 实现，多个实现必须指定\n     */\n    DbType dbType() default DbType.OTHER;\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/OrderBy.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 自动排序，用法与SpringDtaJpa的OrderBy类似\n * 在执行MybatisPlus的方法selectList(),Page()等非手写查询时自动带上.\n * @author Dervish\n * @date 2021-04-13\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface OrderBy {\n\n    /**\n     * 默认倒序，设置 true 顺序\n     */\n    boolean asc() default false;\n\n    /**\n     * 数字越小越靠前\n     */\n    short sort() default Short.MAX_VALUE;\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/SqlCondition.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\n/**\n * SQL 比较条件常量定义类\n *\n * @author hubin\n * @since 2018-01-05\n */\npublic class SqlCondition {\n    /**\n     * 等于\n     */\n    public static final String EQUAL = \"%s=#{%s}\";\n    /**\n     * 不等于\n     */\n    public static final String NOT_EQUAL = \"%s&lt;&gt;#{%s}\";\n    /**\n     * % 两边 %\n     */\n    public static final String LIKE = \"%s LIKE CONCAT('%%',#{%s},'%%')\";\n\n    /**\n     * % 两边 % [oracle使用]\n     */\n    public static final String ORACLE_LIKE = \"%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')\";\n    /**\n     * % 左\n     */\n    public static final String LIKE_LEFT = \"%s LIKE CONCAT('%%',#{%s})\";\n    /**\n     * 右 %\n     */\n    public static final String LIKE_RIGHT = \"%s LIKE CONCAT(#{%s},'%%')\";\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableField.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.ResultMapping;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.apache.ibatis.type.UnknownTypeHandler;\n\nimport java.lang.annotation.*;\n\n/**\n * 表字段标识\n *\n * @author hubin sjy tantan\n * @since 2016-09-09\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface TableField {\n\n    /**\n     * 数据库字段值\n     * <p>\n     * 不需要配置该值的情况:\n     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 true 时,\n     * (mp下默认是true,mybatis默认是false), 数据库字段值.replace(\"_\",\"\").toUpperCase() == 实体属性名.toUpperCase() </li>\n     * <li> 当 {@link com.baomidou.mybatisplus.core.MybatisConfiguration#mapUnderscoreToCamelCase} 为 false 时,\n     * 数据库字段值.toUpperCase() == 实体属性名.toUpperCase() </li>\n     */\n    String value() default \"\";\n\n    /**\n     * 是否为数据库表字段\n     * <p>\n     * 默认 true 存在，false 不存在\n     */\n    boolean exist() default true;\n\n    /**\n     * 字段 where 实体查询比较条件\n     * <p>\n     * 默认 {@link SqlCondition#EQUAL}\n     */\n    String condition() default \"\";\n\n    /**\n     * 字段 update set 部分注入, 该注解优于 el 注解使用\n     * <p>\n     * 例1：@TableField(.. , update=\"%s+1\") 其中 %s 会填充为字段\n     * 输出 SQL 为：update 表 set 字段=字段+1 where ...\n     * <p>\n     * 例2：@TableField(.. , update=\"now()\") 使用数据库时间\n     * 输出 SQL 为：update 表 set 字段=now() where ...\n     */\n    String update() default \"\";\n\n    /**\n     * 字段验证策略之 insert: 当insert操作时，该字段拼接insert语句时的策略\n     * <p>\n     * ALWAYS: 直接拼接 insert into table_a(column) values (#{columnProperty});\n     * NOT_NULL: insert into table_a(<if test=\"columnProperty != null\">column</if>) values (<if test=\"columnProperty != null\">#{columnProperty}</if>)\n     * NOT_EMPTY: insert into table_a(<if test=\"columnProperty != null and columnProperty!=''\">column</if>) values (<if test=\"columnProperty != null and columnProperty!=''\">#{columnProperty}</if>)\n     * NEVER: 该字段不参加 insert 语句\n     * <p>\n     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL\n     *\n     * @since 3.1.2\n     */\n    FieldStrategy insertStrategy() default FieldStrategy.DEFAULT;\n\n    /**\n     * 字段验证策略之 update: 当更新操作时，该字段拼接set语句时的策略\n     * <p>\n     * ALWAYS: 直接拼接 update table_a set column=#{columnProperty}, 属性为null/空string都会被set进去\n     * NOT_NULL: update table_a set <if test=\"columnProperty != null\">column=#{columnProperty}</if>\n     * NOT_EMPTY: update table_a set <if test=\"columnProperty != null and columnProperty!=''\">column=#{columnProperty}</if>\n     * NEVER: 该字段不参加 update 语句\n     * <p>\n     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL\n     *\n     * @since 3.1.2\n     */\n    FieldStrategy updateStrategy() default FieldStrategy.DEFAULT;\n\n    /**\n     * 字段验证策略之 where: 表示该字段在拼接where条件时的策略\n     * <p>\n     * ALWAYS: 直接拼接 column=#{columnProperty}\n     * NOT_NULL: <if test=\"columnProperty != null\">column=#{columnProperty}</if>\n     * NOT_EMPTY: <if test=\"columnProperty != null and columnProperty!=''\">column=#{columnProperty}</if>\n     * NEVER: 该字段不参加 where 语句\n     * <p>\n     * NOT_EMPTY 如果针对的是非 CharSequence 类型的字段则效果等于 NOT_NULL\n     *\n     * @since 3.1.2\n     */\n    FieldStrategy whereStrategy() default FieldStrategy.DEFAULT;\n\n    /**\n     * 字段自动填充策略\n     * <p>\n     * 在对应模式下将会忽略 insertStrategy 或 updateStrategy 的配置,等于断言该字段必有值\n     */\n    FieldFill fill() default FieldFill.DEFAULT;\n\n    /**\n     * 是否进行 select 查询\n     * <p>\n     * 大字段可设置为 false 不加入 select 查询范围\n     */\n    boolean select() default true;\n\n    /**\n     * 是否保持使用全局的 columnFormat 的值\n     * <p>\n     * 只生效于 既设置了全局的 columnFormat 也设置了上面 {@link #value()} 的值\n     * 如果是 false , 全局的 columnFormat 不生效\n     *\n     * @since 3.1.1\n     */\n    boolean keepGlobalFormat() default false;\n\n    /**\n     * {@link ResultMapping#property} and {@link ParameterMapping#property}\n     *\n     * @since 3.4.4\n     */\n    String property() default \"\";\n\n    /**\n     * JDBC类型 (该默认值不代表会按照该值生效),\n     * 只生效于 mp 自动注入的 method,\n     * 建议配合 {@link TableName#autoResultMap()} 一起使用\n     * <p>\n     * {@link ResultMapping#jdbcType} and {@link ParameterMapping#jdbcType}\n     *\n     * @since 3.1.2\n     */\n    JdbcType jdbcType() default JdbcType.UNDEFINED;\n\n    /**\n     * 类型处理器 (该默认值不代表会按照该值生效),\n     * 只生效于 mp 自动注入的 method,\n     * 建议配合 {@link TableName#autoResultMap()} 一起使用\n     * <p>\n     * {@link ResultMapping#typeHandler} and {@link ParameterMapping#typeHandler}\n     *\n     * @since 3.1.2\n     */\n    Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;\n\n    /**\n     * 只在使用了 {@link #typeHandler()} 时判断是否辅助追加 javaType\n     * <p>\n     * 一般情况下不推荐使用\n     * {@link ParameterMapping#javaType}\n     *\n     * @since 3.4.0 @2020-07-23\n     */\n    boolean javaType() default false;\n\n    /**\n     * 指定小数点后保留的位数,\n     * 只生效于 mp 自动注入的 method,\n     * 建议配合 {@link TableName#autoResultMap()} 一起使用\n     * <p>\n     * {@link ParameterMapping#numericScale}\n     *\n     * @since 3.1.2\n     */\n    String numericScale() default \"\";\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableId.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 表主键标识\n *\n * @author hubin\n * @since 2016-01-23\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface TableId {\n\n    /**\n     * 字段名（该值可无）\n     */\n    String value() default \"\";\n\n    /**\n     * 主键类型\n     * {@link IdType}\n     */\n    IdType type() default IdType.NONE;\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableLogic.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 表字段逻辑处理注解（逻辑删除）\n *\n * @author hubin\n * @since 2017-09-09\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface TableLogic {\n\n    /**\n     * 默认逻辑未删除值（该值可无、会自动获取全局配置）\n     */\n    String value() default \"\";\n\n    /**\n     * 默认逻辑删除值（该值可无、会自动获取全局配置）\n     */\n    String delval() default \"\";\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableName.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 数据库表相关\n *\n * @author hubin, hanchunlin\n * @since 2016-01-23\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})\npublic @interface TableName {\n\n    /**\n     * 实体对应的表名\n     */\n    String value() default \"\";\n\n    /**\n     * schema\n     * <p>\n     * 配置此值将覆盖全局配置的 schema\n     *\n     * @since 3.1.1\n     */\n    String schema() default \"\";\n\n    /**\n     * 是否保持使用全局的 tablePrefix 的值\n     * <p> 只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值 </p>\n     * <li> 如果是 false , 全局的 tablePrefix 不生效 </li>\n     *\n     * @since 3.1.1\n     */\n    boolean keepGlobalPrefix() default false;\n\n    /**\n     * 实体映射结果集,\n     * 只生效于 mp 自动注入的 method\n     */\n    String resultMap() default \"\";\n\n    /**\n     * 是否自动构建 resultMap 并使用,\n     * 只生效于 mp 自动注入的 method,\n     * 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入,\n     * 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况\n     *\n     * @since 3.1.2\n     */\n    boolean autoResultMap() default false;\n\n    /**\n     * 只需要的属性名\n     * <p>\n     * 与{@link #excludeProperty()} 二选一配置,都配置了则只有此配置生效\n     *\n     * @since 3.5.10\n     */\n    String[] properties() default {};\n\n    /**\n     * 需要排除的属性名\n     * <p>\n     * 与{@link #properties()} 二选一配置,都配置了则{@link #properties()} 配置生效\n     *\n     * @since 3.3.1\n     */\n    String[] excludeProperty() default {};\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/Version.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.annotation;\n\nimport java.lang.annotation.*;\n\n/**\n * 乐观锁注解\n * <p>\n * 支持的字段类型:\n * long,\n * Long,\n * int,\n * Integer,\n * java.util.Date,\n * java.sql.Timestamp,\n * java.time.LocalDateTime\n * java.time.Instant\n *\n * @author TaoYu\n * @since 2016-01-23\n */\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\npublic @interface Version {\n}\n"
  },
  {
    "path": "mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 注解方法类\n */\npackage com.baomidou.mybatisplus.annotation;\n"
  },
  {
    "path": "mybatis-plus-bom/build.gradle",
    "content": "buildscript {\n    repositories {\n        maven { url \"https://plugins.gradle.org/m2/\" }\n        maven { url \"https://repo.spring.io/plugins-release\" }\n    }\n    dependencies {\n        classpath \"io.spring.gradle:dependency-management-plugin:1.1.6\"\n    }\n}\n\napply plugin: \"io.spring.dependency-management\"\n\ndependencyManagement {\n    dependencies {\n        dependency \"com.baomidou:mybatis-plus-annotation:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-core:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-extension:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-generator:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-solon-plugin:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-boot-starter:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-boot-starter-test:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot-autoconfigure:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot-test-autoconfigure:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot3-starter:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot3-starter-test:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot4-starter:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-spring-boot4-starter-test:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-jsqlparser:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-jsqlparser-4.9:${APP_VERSION}\"\n        dependency \"com.baomidou:mybatis-plus-jsqlparser-5.0:${APP_VERSION}\"\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/build.gradle",
    "content": "dependencies {\n    api project(\":mybatis-plus-annotation\")\n    api \"${lib.mybatis}\"\n\n    implementation \"${lib.cglib}\"\n    implementation \"${lib.'spring-aop'}\"\n    implementation \"${lib.'imadcn'}\"\n\n    testImplementation \"${lib.'logback-classic'}\"\n    testImplementation \"${lib.\"mybatis-spring\"}\"\n    testImplementation \"${lib.'spring-jdbc'}\"\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/InjectorResolver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport org.apache.ibatis.builder.annotation.MethodResolver;\n\n/**\n * 继承 {@link MethodResolver}\n *\n * @author miemie\n * @since 2019-01-05\n */\npublic class InjectorResolver extends MethodResolver {\n\n    private final MybatisMapperAnnotationBuilder annotationBuilder;\n\n    public InjectorResolver(MybatisMapperAnnotationBuilder annotationBuilder) {\n        super(annotationBuilder, null);\n        this.annotationBuilder = annotationBuilder;\n    }\n\n    @Override\n    public void resolve() {\n        annotationBuilder.parserInjector();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.apache.ibatis.binding.MapperRegistry;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMap;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.parsing.XNode;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.transaction.Transaction;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.BiFunction;\nimport java.util.stream.Collectors;\n\n/**\n * replace default Configuration class\n * <p>Caratacus 2016/9/25 replace mapperRegistry</p>\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class MybatisConfiguration extends Configuration {\n    private static final Log logger = LogFactory.getLog(MybatisConfiguration.class);\n    /**\n     * Mapper 注册\n     */\n    protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);\n\n    protected final Map<String, Cache> caches = new StrictMap<>(\"Caches collection\");\n    protected final Map<String, ResultMap> resultMaps = new StrictMap<>(\"Result Maps collection\");\n    protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>(\"Parameter Maps collection\");\n    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>(\"Key Generators collection\");\n    protected final Map<String, XNode> sqlFragments = new StrictMap<>(\"XML fragments parsed from previous mappers\");\n    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(\"Mapped Statements collection\")\n        .conflictMessageProducer((savedValue, targetValue) ->\n            \". please check \" + savedValue.getResource() + \" and \" + targetValue.getResource());\n    /**\n     * 是否生成短key缓存\n     *\n     * @since 3.4.0\n     */\n    @Setter\n    @Getter\n    private boolean useGeneratedShortKey = true;\n\n    public MybatisConfiguration(Environment environment) {\n        this();\n        this.environment = environment;\n    }\n\n    /**\n     * 初始化调用\n     */\n    public MybatisConfiguration() {\n        super();\n        this.mapUnderscoreToCamelCase = true;\n        typeHandlerRegistry.setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);\n        languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);\n    }\n\n    /**\n     * MybatisPlus 加载 SQL 顺序：\n     * <p> 1、加载 XML中的 SQL </p>\n     * <p> 2、加载 SqlProvider 中的 SQL </p>\n     * <p> 3、XmlSql 与 SqlProvider不能包含相同的 SQL </p>\n     * <p>调整后的 SQL优先级：XmlSql > sqlProvider > CurdSql </p>\n     */\n    @Override\n    public void addMappedStatement(MappedStatement ms) {\n        if (mappedStatements.containsKey(ms.getId())) {\n            /*\n             * 说明已加载了xml中的节点； 忽略mapper中的 SqlProvider 数据\n             */\n            logger.error(\"mapper[\" + ms.getId() + \"] is ignored, because it exists, maybe from xml file\");\n            return;\n        }\n        mappedStatements.put(ms.getId(), ms);\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public MapperRegistry getMapperRegistry() {\n        return mybatisMapperRegistry;\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public <T> void addMapper(Class<T> type) {\n        mybatisMapperRegistry.addMapper(type);\n    }\n\n    /**\n     * 新增注入新的 Mapper 信息，新增前会清理之前的缓存信息\n     *\n     * @param type Mapper Type\n     * @deprecated 3.5.8 不建议在实际生产环境中使用.\n     */\n    @Deprecated\n    public <T> void addNewMapper(Class<T> type) {\n        this.removeMapper(type);\n        this.addMapper(type);\n    }\n\n    /**\n     * 移除 Mapper 相关缓存，支持 GroovyClassLoader 动态注入 Mapper\n     *\n     * @param type Mapper Type\n     * @deprecated 3.5.8 不建议在实际生产环境中使用.\n     */\n    @Deprecated\n    public <T> void removeMapper(Class<T> type) {\n        Set<String> mapperRegistryCache = GlobalConfigUtils.getGlobalConfig(this).getMapperRegistryCache();\n        final String mapperType = type.toString();\n        if (mapperRegistryCache.contains(mapperType)) {\n            // 清空实体表信息映射信息\n            TableInfoHelper.remove(ReflectionKit.getSuperClassGenericType(type, Mapper.class, 0));\n\n            // 清空 Mapper 缓存信息\n            this.mybatisMapperRegistry.removeMapper(type);\n            this.loadedResources.remove(type.toString());\n            this.loadedResources.remove(type.getName().replace(StringPool.DOT, StringPool.SLASH) + \".xml\");\n            mapperRegistryCache.remove(mapperType);\n\n            // 清空 Mapper 方法 mappedStatement 缓存信息\n            String typeKey = type.getName() + StringPool.DOT;\n            String simpleName = type.getSimpleName();\n            mappedStatements.keySet().stream().filter(ms -> ms.startsWith(typeKey) || ms.equals(simpleName)).collect(Collectors.toSet()).forEach(mappedStatements::remove);\n            resultMaps.keySet().stream().filter(r -> r.startsWith(typeKey)).collect(Collectors.toSet()).forEach(resultMaps::remove);\n            parameterMaps.keySet().stream().filter(p -> p.startsWith(typeKey)).collect(Collectors.toSet()).forEach(parameterMaps::remove);\n            keyGenerators.keySet().stream().filter(k -> k.startsWith(typeKey)).collect(Collectors.toSet()).forEach(keyGenerators::remove);\n            sqlFragments.keySet().stream().filter(s -> s.startsWith(typeKey)).collect(Collectors.toSet()).forEach(sqlFragments::remove);\n            caches.keySet().stream().filter(p -> p.equals(type.getName()) || p.equals(simpleName)).collect(Collectors.toSet()).forEach(caches::remove);\n        }\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public void addMappers(String packageName, Class<?> superType) {\n        mybatisMapperRegistry.addMappers(packageName, superType);\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public void addMappers(String packageName) {\n        mybatisMapperRegistry.addMappers(packageName);\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {\n        return mybatisMapperRegistry.getMapper(type, sqlSession);\n    }\n\n    /**\n     * 使用自己的 MybatisMapperRegistry\n     */\n    @Override\n    public boolean hasMapper(Class<?> type) {\n        return mybatisMapperRegistry.hasMapper(type);\n    }\n\n    /**\n     * 指定动态SQL生成的默认语言\n     *\n     * @param driver LanguageDriver\n     */\n    @Override\n    public void setDefaultScriptingLanguage(Class<? extends LanguageDriver> driver) {\n        if (driver == null) {\n            driver = MybatisXMLLanguageDriver.class;\n        }\n        getLanguageRegistry().setDefaultDriverClass(driver);\n    }\n\n    @Override\n    public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {\n        if (typeHandler != null) {\n            CompositeEnumTypeHandler.setDefaultEnumTypeHandler(typeHandler);\n        }\n    }\n\n    @Override\n    public void addKeyGenerator(String id, KeyGenerator keyGenerator) {\n        keyGenerators.put(id, keyGenerator);\n    }\n\n    @Override\n    public Collection<String> getKeyGeneratorNames() {\n        return keyGenerators.keySet();\n    }\n\n    @Override\n    public Collection<KeyGenerator> getKeyGenerators() {\n        return keyGenerators.values();\n    }\n\n    @Override\n    public KeyGenerator getKeyGenerator(String id) {\n        return keyGenerators.get(id);\n    }\n\n    @Override\n    public boolean hasKeyGenerator(String id) {\n        return keyGenerators.containsKey(id);\n    }\n\n    @Override\n    public void addCache(Cache cache) {\n        caches.put(cache.getId(), cache);\n    }\n\n    @Override\n    public Collection<String> getCacheNames() {\n        return caches.keySet();\n    }\n\n    @Override\n    public Collection<Cache> getCaches() {\n        return caches.values();\n    }\n\n    @Override\n    public Cache getCache(String id) {\n        return caches.get(id);\n    }\n\n    @Override\n    public boolean hasCache(String id) {\n        return caches.containsKey(id);\n    }\n\n    @Override\n    public void addResultMap(ResultMap rm) {\n        resultMaps.put(rm.getId(), rm);\n        checkLocallyForDiscriminatedNestedResultMaps(rm);\n        checkGloballyForDiscriminatedNestedResultMaps(rm);\n    }\n\n    @Override\n    public Collection<String> getResultMapNames() {\n        return resultMaps.keySet();\n    }\n\n    @Override\n    public Collection<ResultMap> getResultMaps() {\n        return resultMaps.values();\n    }\n\n    @Override\n    public ResultMap getResultMap(String id) {\n        return resultMaps.get(id);\n    }\n\n    @Override\n    public boolean hasResultMap(String id) {\n        return resultMaps.containsKey(id);\n    }\n\n    @Override\n    public void addParameterMap(ParameterMap pm) {\n        parameterMaps.put(pm.getId(), pm);\n    }\n\n    @Override\n    public Collection<String> getParameterMapNames() {\n        return parameterMaps.keySet();\n    }\n\n    @Override\n    public Collection<ParameterMap> getParameterMaps() {\n        return parameterMaps.values();\n    }\n\n    @Override\n    public ParameterMap getParameterMap(String id) {\n        return parameterMaps.get(id);\n    }\n\n    @Override\n    public boolean hasParameterMap(String id) {\n        return parameterMaps.containsKey(id);\n    }\n\n    @Override\n    public Map<String, XNode> getSqlFragments() {\n        return sqlFragments;\n    }\n\n    @Override\n    public Collection<String> getMappedStatementNames() {\n        buildAllStatements();\n        return mappedStatements.keySet();\n    }\n\n    @Override\n    public Collection<MappedStatement> getMappedStatements() {\n        buildAllStatements();\n        return mappedStatements.values();\n    }\n\n    @Override\n    public MappedStatement getMappedStatement(String id) {\n        return this.getMappedStatement(id, true);\n    }\n\n    @Override\n    public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {\n        if (validateIncompleteStatements) {\n            buildAllStatements();\n        }\n        return mappedStatements.get(id);\n    }\n\n    @Override\n    public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {\n        if (validateIncompleteStatements) {\n            buildAllStatements();\n        }\n        return mappedStatements.containsKey(statementName);\n    }\n\n    @Override\n    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {\n        return super.newExecutor(transaction, executorType);\n    }\n\n    // Slow but a one time cost. A better solution is welcome.\n    @Override\n    public void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {\n        if (rm.hasNestedResultMaps()) {\n            final String resultMapId = rm.getId();\n            for (Object resultMapObject : resultMaps.values()) {\n                if (resultMapObject instanceof ResultMap) {\n                    ResultMap entryResultMap = (ResultMap) resultMapObject;\n                    if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {\n                        Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap()\n                            .values();\n                        if (discriminatedResultMapNames.contains(resultMapId)) {\n                            entryResultMap.forceNestedResultMaps();\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    // Slow but a one time cost. A better solution is welcome.\n    @Override\n    protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {\n        if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {\n            for (String discriminatedResultMapName : rm.getDiscriminator().getDiscriminatorMap().values()) {\n                if (hasResultMap(discriminatedResultMapName)) {\n                    ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);\n                    if (discriminatedResultMap.hasNestedResultMaps()) {\n                        rm.forceNestedResultMaps();\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    protected class StrictMap<V> extends ConcurrentHashMap<String, V> {\n\n        private static final long serialVersionUID = -4950446264854982944L;\n        private final String name;\n        private BiFunction<V, V, String> conflictMessageProducer;\n        private final Object AMBIGUITY_INSTANCE = new Object();\n\n        public StrictMap(String name, int initialCapacity, float loadFactor) {\n            super(initialCapacity, loadFactor);\n            this.name = name;\n        }\n\n        public StrictMap(String name, int initialCapacity) {\n            super(initialCapacity);\n            this.name = name;\n        }\n\n        public StrictMap(String name) {\n            super();\n            this.name = name;\n        }\n\n        public StrictMap(String name, Map<String, ? extends V> m) {\n            super(m);\n            this.name = name;\n        }\n\n        /**\n         * Assign a function for producing a conflict error message when contains value with the same key.\n         * <p>\n         * function arguments are 1st is saved value and 2nd is target value.\n         *\n         * @param conflictMessageProducer A function for producing a conflict error message\n         * @return a conflict error message\n         * @since 3.5.0\n         */\n        public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {\n            this.conflictMessageProducer = conflictMessageProducer;\n            return this;\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public V put(String key, V value) {\n            if (containsKey(key)) {\n                throw new IllegalArgumentException(name + \" already contains value for \" + key\n                    + (conflictMessageProducer == null ? StringPool.EMPTY : conflictMessageProducer.apply(super.get(key), value)));\n            }\n            if (useGeneratedShortKey) {\n                if (key.contains(StringPool.DOT)) {\n                    final String shortKey = getShortName(key);\n                    if (super.get(shortKey) == null) {\n                        super.put(shortKey, value);\n                    } else {\n                        super.put(shortKey, (V) AMBIGUITY_INSTANCE);\n                    }\n                }\n            }\n            return super.put(key, value);\n        }\n\n        @Override\n        public boolean containsKey(Object key) {\n            if (key == null) {\n                return false;\n            }\n            return super.get(key) != null;\n        }\n\n        @Override\n        public V get(Object key) {\n            V value = super.get(key);\n            if (value == null) {\n                throw new IllegalArgumentException(name + \" does not contain value for \" + key);\n            }\n            if (useGeneratedShortKey && AMBIGUITY_INSTANCE == value) {\n                throw new IllegalArgumentException(key + \" is ambiguous in \" + name\n                    + \" (try using the full name including the namespace, or rename one of the entries)\");\n            }\n            return value;\n        }\n\n        private String getShortName(String key) {\n            final String[] keyParts = key.split(\"\\\\.\");\n            return keyParts[keyParts.length - 1];\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport org.apache.ibatis.annotations.*;\nimport org.apache.ibatis.annotations.ResultMap;\nimport org.apache.ibatis.annotations.Options.FlushCachePolicy;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.apache.ibatis.builder.BuilderException;\nimport org.apache.ibatis.builder.CacheRefResolver;\nimport org.apache.ibatis.builder.IncompleteElementException;\nimport org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;\nimport org.apache.ibatis.builder.annotation.ProviderSqlSource;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.executor.keygen.SelectKeyGenerator;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.mapping.*;\nimport org.apache.ibatis.parsing.PropertyParser;\nimport org.apache.ibatis.reflection.TypeParameterResolver;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.apache.ibatis.type.UnknownTypeHandler;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.*;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n\n/**\n * 继承\n * <p>\n * 只重写了 {@link MapperAnnotationBuilder#parse} 和 #getReturnType\n * 没有XML配置文件注入基础CRUD方法\n * </p>\n *\n * @author Caratacus\n * @since 2017-01-04\n */\npublic class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {\n\n    private static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream\n        .of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,\n            InsertProvider.class, DeleteProvider.class)\n        .collect(Collectors.toSet());\n\n    private final Configuration configuration;\n    private final MybatisMapperBuilderAssistant assistant;\n    private final Class<?> type;\n\n    public MybatisMapperAnnotationBuilder(Configuration configuration, Class<?> type) {\n        super(configuration, type);\n        String resource = type.getName().replace(StringPool.DOT, StringPool.SLASH) + \".java (best guess)\";\n        this.assistant = new MybatisMapperBuilderAssistant(configuration, resource);\n        this.configuration = configuration;\n        this.type = type;\n    }\n\n    @Override\n    public void parse() {\n        String resource = type.toString();\n        if (!configuration.isResourceLoaded(resource)) {\n            loadXmlResource();\n            configuration.addLoadedResource(resource);\n            String mapperName = type.getName();\n            assistant.setCurrentNamespace(mapperName);\n            parseCache();\n            parseCacheRef();\n            IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type);\n            for (Method method : type.getMethods()) {\n                InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method);\n                if (!canHaveStatement(method)) {\n                    continue;\n                }\n                if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()\n                    && method.getAnnotation(ResultMap.class) == null) {\n                    parseResultMap(method);\n                }\n                try {\n                    parseStatement(method);\n                } catch (IncompleteElementException e) {\n                    configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));\n                }\n            }\n            try {\n                // https://github.com/baomidou/mybatis-plus/issues/3038\n                if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {\n                    parserInjector();\n                }\n            } catch (IncompleteElementException e) {\n                configuration.addIncompleteMethod(new InjectorResolver(this));\n            }\n        }\n        configuration.parsePendingMethods(false);\n    }\n\n    void parserInjector() {\n        GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);\n    }\n\n    private static boolean canHaveStatement(Method method) {\n        // issue #237\n        return !method.isBridge() && !method.isDefault();\n    }\n\n\n    private void loadXmlResource() {\n        // Spring may not know the real resource name so we check a flag\n        // to prevent loading again a resource twice\n        // this flag is set at XMLMapperBuilder#bindMapperForNamespace\n        if (!configuration.isResourceLoaded(\"namespace:\" + type.getName())) {\n            String xmlResource = type.getName().replace(StringPool.DOT, StringPool.SLASH) + \".xml\";\n            // #1347\n            InputStream inputStream = type.getResourceAsStream(StringPool.SLASH + xmlResource);\n            if (inputStream == null) {\n                // Search XML mapper that is not in the module but in the classpath.\n                try {\n                    inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);\n                } catch (IOException e2) {\n                    // ignore, resource is not required\n                }\n            }\n            if (inputStream != null) {\n                MybatisXMLMapperBuilder xmlParser = new MybatisXMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());\n                xmlParser.parse();\n            }\n        }\n    }\n\n    private void parseCache() {\n        CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);\n        if (cacheDomain != null) {\n            Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();\n            Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();\n            Properties props = convertToProperties(cacheDomain.properties());\n            assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size,\n                cacheDomain.readWrite(), cacheDomain.blocking(), props);\n        }\n    }\n\n    private Properties convertToProperties(Property[] properties) {\n        if (properties.length == 0) {\n            return null;\n        }\n        Properties props = new Properties();\n        for (Property property : properties) {\n            props.setProperty(property.name(), PropertyParser.parse(property.value(), configuration.getVariables()));\n        }\n        return props;\n    }\n\n    private void parseCacheRef() {\n        CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);\n        if (cacheDomainRef != null) {\n            Class<?> refType = cacheDomainRef.value();\n            String refName = cacheDomainRef.name();\n            if (refType == void.class && refName.isEmpty()) {\n                throw new BuilderException(\"Should be specified either value() or name() attribute in the @CacheNamespaceRef\");\n            }\n            if (refType != void.class && !refName.isEmpty()) {\n                throw new BuilderException(\"Cannot use both value() and name() attribute in the @CacheNamespaceRef\");\n            }\n            String namespace = refType != void.class ? refType.getName() : refName;\n            try {\n                assistant.useCacheRef(namespace);\n            } catch (IncompleteElementException e) {\n                configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));\n            }\n        }\n    }\n\n    private String parseResultMap(Method method) {\n        Class<?> returnType = getReturnType(method, type);\n        Arg[] args = method.getAnnotationsByType(Arg.class);\n        Result[] results = method.getAnnotationsByType(Result.class);\n        TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);\n        String resultMapId = generateResultMapName(method);\n        applyResultMap(resultMapId, returnType, args, results, typeDiscriminator);\n        return resultMapId;\n    }\n\n    private String generateResultMapName(Method method) {\n        Results results = method.getAnnotation(Results.class);\n        if (results != null && !results.id().isEmpty()) {\n            return type.getName() + \".\" + results.id();\n        }\n        StringBuilder suffix = new StringBuilder();\n        for (Class<?> c : method.getParameterTypes()) {\n            suffix.append(\"-\");\n            suffix.append(c.getSimpleName());\n        }\n        if (suffix.length() < 1) {\n            suffix.append(\"-void\");\n        }\n        return type.getName() + \".\" + method.getName() + suffix;\n    }\n\n    private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results,\n                                TypeDiscriminator discriminator) {\n        List<ResultMapping> resultMappings = new ArrayList<>();\n        applyConstructorArgs(args, returnType, resultMappings);\n        applyResults(results, returnType, resultMappings);\n        Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);\n        // TODO add AutoMappingBehaviour\n        assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null);\n        createDiscriminatorResultMaps(resultMapId, returnType, discriminator);\n    }\n\n    private void createDiscriminatorResultMaps(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {\n        if (discriminator != null) {\n            for (Case c : discriminator.cases()) {\n                String caseResultMapId = resultMapId + \"-\" + c.value();\n                List<ResultMapping> resultMappings = new ArrayList<>();\n                // issue #136\n                applyConstructorArgs(c.constructArgs(), resultType, resultMappings);\n                applyResults(c.results(), resultType, resultMappings);\n                // TODO add AutoMappingBehaviour\n                assistant.addResultMap(caseResultMapId, c.type(), resultMapId, null, resultMappings, null);\n            }\n        }\n    }\n\n    private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {\n        if (discriminator != null) {\n            String column = discriminator.column();\n            Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();\n            JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();\n            @SuppressWarnings(\"unchecked\")\n            Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (discriminator\n                .typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler());\n            Case[] cases = discriminator.cases();\n            Map<String, String> discriminatorMap = new HashMap<>();\n            for (Case c : cases) {\n                String value = c.value();\n                String caseResultMapId = resultMapId + \"-\" + value;\n                discriminatorMap.put(value, caseResultMapId);\n            }\n            return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);\n        }\n        return null;\n    }\n\n    void parseStatement(Method method) {\n        final Class<?> parameterTypeClass = getParameterType(method);\n        final LanguageDriver languageDriver = getLanguageDriver(method);\n\n        getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {\n            final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass,\n                languageDriver, method);\n            final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();\n            final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation())\n                .orElse(null);\n            final String mappedStatementId = type.getName() + \".\" + method.getName();\n\n            final KeyGenerator keyGenerator;\n            String keyProperty = null;\n            String keyColumn = null;\n            if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {\n                // first check for SelectKey annotation - that overrides everything else\n                SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class)\n                    .map(x -> (SelectKey) x.getAnnotation()).orElse(null);\n                if (selectKey != null) {\n                    keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method),\n                        languageDriver);\n                    keyProperty = selectKey.keyProperty();\n                } else if (options == null) {\n                    keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;\n                } else {\n                    keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;\n                    keyProperty = options.keyProperty();\n                    keyColumn = options.keyColumn();\n                }\n            } else {\n                keyGenerator = NoKeyGenerator.INSTANCE;\n            }\n\n            Integer fetchSize = null;\n            Integer timeout = null;\n            StatementType statementType = StatementType.PREPARED;\n            ResultSetType resultSetType = configuration.getDefaultResultSetType();\n            boolean isSelect = sqlCommandType == SqlCommandType.SELECT;\n            boolean flushCache = !isSelect;\n            boolean useCache = isSelect;\n            if (options != null) {\n                if (FlushCachePolicy.TRUE.equals(options.flushCache())) {\n                    flushCache = true;\n                } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {\n                    flushCache = false;\n                }\n                useCache = options.useCache();\n                // issue #348\n                fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null;\n                timeout = options.timeout() > -1 ? options.timeout() : null;\n                statementType = options.statementType();\n                if (options.resultSetType() != ResultSetType.DEFAULT) {\n                    resultSetType = options.resultSetType();\n                }\n            }\n\n            String resultMapId = null;\n            if (isSelect) {\n                ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);\n                if (resultMapAnnotation != null) {\n                    resultMapId = String.join(\",\", resultMapAnnotation.value());\n                } else {\n                    resultMapId = generateResultMapName(method);\n                }\n            }\n\n            assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout,\n                // ParameterMapID\n                null, parameterTypeClass, resultMapId, getReturnType(method, type), resultSetType, flushCache, useCache,\n                // TODO gcode issue #577\n                false, keyGenerator, keyProperty, keyColumn, statementAnnotation.getDatabaseId(), languageDriver,\n                // ResultSets\n                options != null ? nullOrEmpty(options.resultSets()) : null, statementAnnotation.isDirtySelect());\n        });\n    }\n\n    private LanguageDriver getLanguageDriver(Method method) {\n        Lang lang = method.getAnnotation(Lang.class);\n        Class<? extends LanguageDriver> langClass = null;\n        if (lang != null) {\n            langClass = lang.value();\n        }\n        return configuration.getLanguageDriver(langClass);\n    }\n\n    private Class<?> getParameterType(Method method) {\n        Class<?> parameterType = null;\n        Class<?>[] parameterTypes = method.getParameterTypes();\n        for (Class<?> currentParameterType : parameterTypes) {\n            if (!RowBounds.class.isAssignableFrom(currentParameterType)\n                && !ResultHandler.class.isAssignableFrom(currentParameterType)) {\n                if (parameterType == null) {\n                    parameterType = currentParameterType;\n                } else {\n                    // issue #135\n                    parameterType = MapperMethod.ParamMap.class;\n                }\n            }\n        }\n        return parameterType;\n    }\n\n    private static Class<?> getReturnType(Method method, Class<?> type) {\n        Class<?> returnType = method.getReturnType();\n        Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);\n        if (resolvedReturnType instanceof Class) {\n            returnType = (Class<?>) resolvedReturnType;\n            if (returnType.isArray()) {\n                returnType = returnType.getComponentType();\n            }\n            // gcode issue #508\n            if (void.class.equals(returnType)) {\n                ResultType rt = method.getAnnotation(ResultType.class);\n                if (rt != null) {\n                    returnType = rt.value();\n                }\n            }\n        } else if (resolvedReturnType instanceof ParameterizedType) {\n            ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;\n            Class<?> rawType = (Class<?>) parameterizedType.getRawType();\n            if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                if (actualTypeArguments != null && actualTypeArguments.length == 1) {\n                    Type returnTypeParameter = actualTypeArguments[0];\n                    if (returnTypeParameter instanceof Class<?>) {\n                        returnType = (Class<?>) returnTypeParameter;\n                    } else if (returnTypeParameter instanceof ParameterizedType) {\n                        // (gcode issue #443) actual type can be a also a parameterized type\n                        returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                    } else if (returnTypeParameter instanceof GenericArrayType) {\n                        Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();\n                        // (gcode issue #525) support List<byte[]>\n                        returnType = Array.newInstance(componentType, 0).getClass();\n                    }\n                }\n            } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {\n                // (gcode issue 504) Do not look into Maps if there is not MapKey annotation\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                if (actualTypeArguments != null && actualTypeArguments.length == 2) {\n                    Type returnTypeParameter = actualTypeArguments[1];\n                    if (returnTypeParameter instanceof Class<?>) {\n                        returnType = (Class<?>) returnTypeParameter;\n                    } else if (returnTypeParameter instanceof ParameterizedType) {\n                        // (gcode issue 443) actual type can be a also a parameterized type\n                        returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                    }\n                }\n            } else if (Optional.class.equals(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                Type returnTypeParameter = actualTypeArguments[0];\n                if (returnTypeParameter instanceof Class<?>) {\n                    returnType = (Class<?>) returnTypeParameter;\n                }\n            }\n            else if (IPage.class.isAssignableFrom(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                Type returnTypeParameter = actualTypeArguments[0];\n                if (returnTypeParameter instanceof Class<?>) {\n                    returnType = (Class<?>) returnTypeParameter;\n                } else if (returnTypeParameter instanceof ParameterizedType) {\n                    returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                }\n            }\n        }\n\n        return returnType;\n    }\n\n    private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {\n        for (Result result : results) {\n            List<ResultFlag> flags = new ArrayList<>();\n            if (result.id()) {\n                flags.add(ResultFlag.ID);\n            }\n            @SuppressWarnings(\"unchecked\")\n            Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (result\n                .typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler());\n            boolean hasNestedResultMap = hasNestedResultMap(result);\n            ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(result.property()),\n                nullOrEmpty(result.column()), result.javaType() == void.class ? null : result.javaType(),\n                result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),\n                hasNestedSelect(result) ? nestedSelectId(result) : null,\n                hasNestedResultMap ? nestedResultMapId(result) : null, null,\n                hasNestedResultMap ? findColumnPrefix(result) : null, typeHandler, flags, null, null, isLazy(result));\n            resultMappings.add(resultMapping);\n        }\n    }\n\n    private String findColumnPrefix(Result result) {\n        String columnPrefix = result.one().columnPrefix();\n        if (columnPrefix.length() < 1) {\n            columnPrefix = result.many().columnPrefix();\n        }\n        return columnPrefix;\n    }\n\n    private String nestedResultMapId(Result result) {\n        String resultMapId = result.one().resultMap();\n        if (resultMapId.length() < 1) {\n            resultMapId = result.many().resultMap();\n        }\n        if (!resultMapId.contains(StringPool.DOT)) {\n            resultMapId = type.getName() + StringPool.DOT + resultMapId;\n        }\n        return resultMapId;\n    }\n\n    private boolean hasNestedResultMap(Result result) {\n        if (result.one().resultMap().length() > 0 && result.many().resultMap().length() > 0) {\n            throw new BuilderException(\"Cannot use both @One and @Many annotations in the same @Result\");\n        }\n        return result.one().resultMap().length() > 0 || result.many().resultMap().length() > 0;\n    }\n\n    private String nestedSelectId(Result result) {\n        String nestedSelect = result.one().select();\n        if (nestedSelect.length() < 1) {\n            nestedSelect = result.many().select();\n        }\n        if (!nestedSelect.contains(StringPool.DOT)) {\n            nestedSelect = type.getName() + StringPool.DOT + nestedSelect;\n        }\n        return nestedSelect;\n    }\n\n    private boolean isLazy(Result result) {\n        boolean isLazy = configuration.isLazyLoadingEnabled();\n        if (result.one().select().length() > 0 && FetchType.DEFAULT != result.one().fetchType()) {\n            isLazy = result.one().fetchType() == FetchType.LAZY;\n        } else if (result.many().select().length() > 0 && FetchType.DEFAULT != result.many().fetchType()) {\n            isLazy = result.many().fetchType() == FetchType.LAZY;\n        }\n        return isLazy;\n    }\n\n    private boolean hasNestedSelect(Result result) {\n        if (result.one().select().length() > 0 && result.many().select().length() > 0) {\n            throw new BuilderException(\"Cannot use both @One and @Many annotations in the same @Result\");\n        }\n        return result.one().select().length() > 0 || result.many().select().length() > 0;\n    }\n\n    private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {\n        for (Arg arg : args) {\n            List<ResultFlag> flags = new ArrayList<>();\n            flags.add(ResultFlag.CONSTRUCTOR);\n            if (arg.id()) {\n                flags.add(ResultFlag.ID);\n            }\n            @SuppressWarnings(\"unchecked\")\n            Class<? extends TypeHandler<?>> typeHandler = (Class<? extends TypeHandler<?>>) (arg\n                .typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler());\n            ResultMapping resultMapping = assistant.buildResultMapping(resultType, nullOrEmpty(arg.name()),\n                nullOrEmpty(arg.column()), arg.javaType() == void.class ? null : arg.javaType(),\n                arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(), nullOrEmpty(arg.select()),\n                nullOrEmpty(arg.resultMap()), null, nullOrEmpty(arg.columnPrefix()), typeHandler, flags, null, null, false);\n            resultMappings.add(resultMapping);\n        }\n    }\n\n    private String nullOrEmpty(String value) {\n        return value == null || value.trim().length() == 0 ? null : value;\n    }\n\n    private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId,\n                                                   Class<?> parameterTypeClass, LanguageDriver languageDriver) {\n        String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;\n        Class<?> resultTypeClass = selectKeyAnnotation.resultType();\n        StatementType statementType = selectKeyAnnotation.statementType();\n        String keyProperty = selectKeyAnnotation.keyProperty();\n        String keyColumn = selectKeyAnnotation.keyColumn();\n        boolean executeBefore = selectKeyAnnotation.before();\n\n        // defaults\n        boolean useCache = false;\n        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;\n        Integer fetchSize = null;\n        Integer timeout = null;\n        boolean flushCache = false;\n        String parameterMap = null;\n        String resultMap = null;\n        ResultSetType resultSetTypeEnum = null;\n        String databaseId = selectKeyAnnotation.databaseId().isEmpty() ? null : selectKeyAnnotation.databaseId();\n\n        SqlSource sqlSource = buildSqlSource(selectKeyAnnotation, parameterTypeClass, languageDriver, null);\n        SqlCommandType sqlCommandType = SqlCommandType.SELECT;\n\n        assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,\n            parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, false, keyGenerator,\n            keyProperty, keyColumn, databaseId, languageDriver, null, false);\n\n        id = assistant.applyCurrentNamespace(id, false);\n\n        MappedStatement keyStatement = configuration.getMappedStatement(id, false);\n        SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);\n        configuration.addKeyGenerator(id, answer);\n        return answer;\n    }\n\n    private SqlSource buildSqlSource(Annotation annotation, Class<?> parameterType, LanguageDriver languageDriver,\n                                     Method method) {\n        if (annotation instanceof Select) {\n            return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver);\n        }\n        if (annotation instanceof Update) {\n            return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver);\n        } else if (annotation instanceof Insert) {\n            return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver);\n        } else if (annotation instanceof Delete) {\n            return buildSqlSourceFromStrings(((Delete) annotation).value(), parameterType, languageDriver);\n        } else if (annotation instanceof SelectKey) {\n            return buildSqlSourceFromStrings(((SelectKey) annotation).statement(), parameterType, languageDriver);\n        }\n        return new ProviderSqlSource(assistant.getConfiguration(), annotation, type, method);\n    }\n\n    private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass,\n                                                LanguageDriver languageDriver) {\n        return languageDriver.createSqlSource(configuration, String.join(\" \", strings).trim(), parameterTypeClass);\n    }\n\n    @SafeVarargs\n    private final Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean errorIfNoMatch,\n                                                                                           Class<? extends Annotation>... targetTypes) {\n        return getAnnotationWrapper(method, errorIfNoMatch, Arrays.asList(targetTypes));\n    }\n\n    private Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean errorIfNoMatch,\n                                                                                     Collection<Class<? extends Annotation>> targetTypes) {\n        String databaseId = configuration.getDatabaseId();\n        Map<String, AnnotationWrapper> statementAnnotations = targetTypes.stream()\n            .flatMap(x -> Arrays.stream(method.getAnnotationsByType(x))).map(AnnotationWrapper::new)\n            .collect(Collectors.toMap(AnnotationWrapper::getDatabaseId, x -> x, (existing, duplicate) -> {\n                throw new BuilderException(\n                    String.format(\"Detected conflicting annotations '%s' and '%s' on '%s'.\", existing.getAnnotation(),\n                        duplicate.getAnnotation(), method.getDeclaringClass().getName() + \".\" + method.getName()));\n            }));\n        AnnotationWrapper annotationWrapper = null;\n        if (databaseId != null) {\n            annotationWrapper = statementAnnotations.get(databaseId);\n        }\n        if (annotationWrapper == null) {\n            annotationWrapper = statementAnnotations.get(\"\");\n        }\n        if (errorIfNoMatch && annotationWrapper == null && !statementAnnotations.isEmpty()) {\n            // Annotations exist, but there is no matching one for the specified databaseId\n            throw new BuilderException(String.format(\n                \"Could not find a statement annotation that correspond a current database or default statement on method '%s.%s'. Current database id is [%s].\",\n                method.getDeclaringClass().getName(), method.getName(), databaseId));\n        }\n        return Optional.ofNullable(annotationWrapper);\n    }\n\n    public static Class<?> getMethodReturnType(String mapperFqn, String localStatementId) {\n        if (mapperFqn == null || localStatementId == null) {\n            return null;\n        }\n        try {\n            Class<?> mapperClass = Resources.classForName(mapperFqn);\n            for (Method method : mapperClass.getMethods()) {\n                if (method.getName().equals(localStatementId) && canHaveStatement(method)) {\n                    return getReturnType(method, mapperClass);\n                }\n            }\n        } catch (ClassNotFoundException e) {\n            // No corresponding mapper interface which is OK\n        }\n        return null;\n    }\n\n    private static class AnnotationWrapper {\n        private final Annotation annotation;\n        private final String databaseId;\n        private final SqlCommandType sqlCommandType;\n        private boolean dirtySelect;\n\n        AnnotationWrapper(Annotation annotation) {\n            this.annotation = annotation;\n            if (annotation instanceof Select) {\n                databaseId = ((Select) annotation).databaseId();\n                sqlCommandType = SqlCommandType.SELECT;\n                dirtySelect = ((Select) annotation).affectData();\n            } else if (annotation instanceof Update) {\n                databaseId = ((Update) annotation).databaseId();\n                sqlCommandType = SqlCommandType.UPDATE;\n            } else if (annotation instanceof Insert) {\n                databaseId = ((Insert) annotation).databaseId();\n                sqlCommandType = SqlCommandType.INSERT;\n            } else if (annotation instanceof Delete) {\n                databaseId = ((Delete) annotation).databaseId();\n                sqlCommandType = SqlCommandType.DELETE;\n            } else if (annotation instanceof SelectProvider) {\n                databaseId = ((SelectProvider) annotation).databaseId();\n                sqlCommandType = SqlCommandType.SELECT;\n                dirtySelect = ((SelectProvider) annotation).affectData();\n            } else if (annotation instanceof UpdateProvider) {\n                databaseId = ((UpdateProvider) annotation).databaseId();\n                sqlCommandType = SqlCommandType.UPDATE;\n            } else if (annotation instanceof InsertProvider) {\n                databaseId = ((InsertProvider) annotation).databaseId();\n                sqlCommandType = SqlCommandType.INSERT;\n            } else if (annotation instanceof DeleteProvider) {\n                databaseId = ((DeleteProvider) annotation).databaseId();\n                sqlCommandType = SqlCommandType.DELETE;\n            } else {\n                sqlCommandType = SqlCommandType.UNKNOWN;\n                if (annotation instanceof Options) {\n                    databaseId = ((Options) annotation).databaseId();\n                } else if (annotation instanceof SelectKey) {\n                    databaseId = ((SelectKey) annotation).databaseId();\n                } else {\n                    databaseId = StringPool.EMPTY;\n                }\n            }\n        }\n\n        Annotation getAnnotation() {\n            return annotation;\n        }\n\n        SqlCommandType getSqlCommandType() {\n            return sqlCommandType;\n        }\n\n        String getDatabaseId() {\n            return databaseId;\n        }\n\n        boolean isDirtySelect() {\n            return dirtySelect;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperBuilderAssistant.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisUtils;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.mapping.ResultFlag;\nimport org.apache.ibatis.mapping.ResultMapping;\nimport org.apache.ibatis.reflection.MetaClass;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.apache.ibatis.type.UnknownTypeHandler;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.StringTokenizer;\n\n/**\n * 重写了原生方法.\n *\n * @author nieqiurong\n * @see MapperBuilderAssistant\n * @since 3.5.6\n */\npublic class MybatisMapperBuilderAssistant extends MapperBuilderAssistant {\n\n    public MybatisMapperBuilderAssistant(Configuration configuration, String resource) {\n        super(configuration, resource);\n    }\n\n    @Override\n    public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, Class<?> javaType,\n                                            JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix,\n                                            Class<? extends TypeHandler<?>> typeHandler, List<ResultFlag> flags, String resultSet, String foreignColumn,\n                                            boolean lazy) {\n        Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);\n        TypeHandler<?> typeHandlerInstance = null;\n        if (typeHandler != null) {\n            if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) {\n                try {\n                    Field field = resultType.getDeclaredField(property);\n                    typeHandlerInstance = MybatisUtils.newJsonTypeHandler(typeHandler, javaTypeClass, field);\n                } catch (NoSuchFieldException e) {\n                    //ignore 降级兼容处理\n                    typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);\n                }\n            } else {\n                typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);\n            }\n        }\n        List<ResultMapping> composites;\n        if ((nestedSelect == null || nestedSelect.isEmpty()) && (foreignColumn == null || foreignColumn.isEmpty())) {\n            composites = Collections.emptyList();\n        } else {\n            composites = parseCompositeColumnName(column);\n        }\n        return new ResultMapping.Builder(configuration, property, column, javaTypeClass).jdbcType(jdbcType)\n            .nestedQueryId(applyCurrentNamespace(nestedSelect, true))\n            .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)).resultSet(resultSet)\n            .typeHandler(typeHandlerInstance).flags(flags == null ? new ArrayList<>() : flags).composites(composites)\n            .notNullColumns(parseMultipleColumnNames(notNullColumn)).columnPrefix(columnPrefix).foreignColumn(foreignColumn)\n            .lazy(lazy).build();\n    }\n\n\n    private Set<String> parseMultipleColumnNames(String columnName) {\n        Set<String> columns = new HashSet<>();\n        if (columnName != null) {\n            if (columnName.indexOf(',') > -1) {\n                StringTokenizer parser = new StringTokenizer(columnName, \"{}, \", false);\n                while (parser.hasMoreTokens()) {\n                    String column = parser.nextToken();\n                    columns.add(column);\n                }\n            } else {\n                columns.add(columnName);\n            }\n        }\n        return columns;\n    }\n\n\n    private List<ResultMapping> parseCompositeColumnName(String columnName) {\n        List<ResultMapping> composites = new ArrayList<>();\n        if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) {\n            StringTokenizer parser = new StringTokenizer(columnName, \"{}=, \", false);\n            while (parser.hasMoreTokens()) {\n                String property = parser.nextToken();\n                String column = parser.nextToken();\n                ResultMapping complexResultMapping = new ResultMapping.Builder(configuration, property, column,\n                    configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build();\n                composites.add(complexResultMapping);\n            }\n        }\n        return composites;\n    }\n\n    private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {\n        if (javaType == null && property != null) {\n            try {\n                MetaClass metaResultType = MetaClass.forClass(resultType, configuration.getReflectorFactory());\n                javaType = metaResultType.getSetterType(property);\n            } catch (Exception e) {\n                // ignore, following null check statement will deal with the situation\n            }\n        }\n        if (javaType == null) {\n            javaType = Object.class;\n        }\n        return javaType;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory;\nimport org.apache.ibatis.binding.BindingException;\nimport org.apache.ibatis.binding.MapperRegistry;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSession;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 继承至MapperRegistry\n *\n * @author Caratacus hubin\n * @since 2017-04-19\n */\npublic class MybatisMapperRegistry extends MapperRegistry {\n\n    private final Configuration config;\n\n    private final Map<Class<?>, MybatisMapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>();\n\n    public MybatisMapperRegistry(Configuration config) {\n        super(config);\n        this.config = config;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {\n        // fix https://github.com/baomidou/mybatis-plus/issues/4247\n        MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);\n        if (mapperProxyFactory == null) {\n            mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.entrySet().stream()\n                .filter(t -> t.getKey().getName().equals(type.getName())).findFirst().map(Map.Entry::getValue)\n                .orElseThrow(() -> new BindingException(\"Type \" + type + \" is not known to the MybatisPlusMapperRegistry.\"));\n        }\n        try {\n            return mapperProxyFactory.newInstance(sqlSession);\n        } catch (Exception e) {\n            throw new BindingException(\"Error getting mapper instance. Cause: \" + e, e);\n        }\n    }\n\n    @Override\n    public <T> boolean hasMapper(Class<T> type) {\n        return knownMappers.containsKey(type);\n    }\n\n    /**\n     * 清空 Mapper 缓存信息\n     */\n    protected <T> void removeMapper(Class<T> type) {\n        knownMappers.entrySet().stream().filter(t -> t.getKey().getName().equals(type.getName()))\n            .findFirst().ifPresent(t -> knownMappers.remove(t.getKey()));\n    }\n\n    @Override\n    public <T> void addMapper(Class<T> type) {\n        if (type.isInterface()) {\n            if (hasMapper(type)) {\n                return;\n//                throw new BindingException(\"Type \" + type + \" is already known to the MapperRegistry.\");\n            }\n            boolean loadCompleted = false;\n            try {\n                knownMappers.put(type, new MybatisMapperProxyFactory<>(type));\n                // It's important that the type is added before the parser is run\n                // otherwise the binding may automatically be attempted by the\n                // mapper parser. If the type is already known, it won't try.\n                MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);\n                parser.parse();\n                loadCompleted = true;\n            } finally {\n                if (!loadCompleted) {\n                    knownMappers.remove(type);\n                }\n            }\n        }\n    }\n\n    /**\n     * 使用自己的 knownMappers\n     */\n    @Override\n    public Collection<Class<?>> getMappers() {\n        return Collections.unmodifiableCollection(knownMappers.keySet());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMethodResolver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport org.apache.ibatis.builder.annotation.MethodResolver;\n\nimport java.lang.reflect.Method;\n\n/**\n * 继承 {@link MethodResolver}\n *\n * @author miemie\n * @since 2019-01-05\n */\npublic class MybatisMethodResolver extends MethodResolver {\n\n    private final MybatisMapperAnnotationBuilder annotationBuilder;\n    private final Method method;\n\n    public MybatisMethodResolver(MybatisMapperAnnotationBuilder annotationBuilder, Method method) {\n        super(null, null);\n        this.annotationBuilder = annotationBuilder;\n        this.method = method;\n    }\n\n    @Override\n    public void resolve() {\n        annotationBuilder.parseStatement(method);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisParameterHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.ArrayUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.ognl.OgnlOps;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.scripting.defaults.DefaultParameterHandler;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.SimpleTypeRegistry;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 自定义 ParameterHandler 重装构造函数，填充插入方法主键 ID\n *\n * @author nieqiuqiu 2020/6/5\n * @since 3.4.0\n */\npublic class MybatisParameterHandler extends DefaultParameterHandler {\n\n    /**\n     * 填充的key值\n     *\n     * @since 3.5.3.2\n     * @deprecated 3.5.4\n     */\n    @Deprecated\n    public static final String[] COLLECTION_KEYS = new String[]{\"collection\", \"coll\", \"list\", \"array\"};\n\n    private final Configuration configuration;\n    private final SqlCommandType sqlCommandType;\n    private final MappedStatement mappedStatement;\n    private final Log log;\n\n    public MybatisParameterHandler(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) {\n        super(mappedStatement, parameter, boundSql);\n        this.mappedStatement = mappedStatement;\n        this.log = mappedStatement.getStatementLog();\n        this.configuration = mappedStatement.getConfiguration();\n        this.sqlCommandType = mappedStatement.getSqlCommandType();\n        processParameter(parameter);\n    }\n\n    public void processParameter(Object parameter) {\n        /* 只处理插入或更新操作 */\n        if (parameter != null && !SimpleTypeRegistry.isSimpleType(parameter.getClass())) {\n            if (SqlCommandType.INSERT == this.sqlCommandType || SqlCommandType.UPDATE == this.sqlCommandType) {\n                extractParameters(parameter).forEach(this::process);\n            }\n        }\n    }\n\n    private void process(Object parameter) {\n        if (parameter != null) {\n            TableInfo tableInfo = null;\n            Object entity = parameter;\n            if (parameter instanceof Map) {\n                // 处理单参数使用注解标记的时候，尝试提取et来获取实体参数\n                Map<?, ?> map = (Map<?, ?>) parameter;\n                Object et = null;\n                if(map.containsKey(Constants.ENTITY)){\n                    et = map.get(Constants.ENTITY);\n\n                } else if(map.containsKey(Constants.MP_FILL_ET)){\n                    et = map.get(Constants.MP_FILL_ET);\n                }\n                if (et != null) {\n                    entity = et;\n                    tableInfo = TableInfoHelper.getTableInfo(entity.getClass());\n                }\n            } else {\n                tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());\n            }\n            if (tableInfo != null) {\n                //到这里就应该转换到实体参数对象了,因为填充和ID处理都是针对实体对象处理的,不用传递原参数对象下去.\n                MetaObject metaObject = this.configuration.newMetaObject(entity);\n                if (SqlCommandType.INSERT == this.sqlCommandType) {\n                    populateKeys(tableInfo, metaObject, entity);\n                    insertFill(metaObject, tableInfo);\n                } else {\n                    updateFill(metaObject, tableInfo);\n                }\n            }\n        }\n    }\n\n    protected void populateKeys(TableInfo tableInfo, MetaObject metaObject, Object entity) {\n        final IdType idType = tableInfo.getIdType();\n        final String keyProperty = tableInfo.getKeyProperty();\n        if (StringUtils.isNotBlank(keyProperty) && null != idType && idType.getKey() >= 3) {\n            final IdentifierGenerator identifierGenerator = GlobalConfigUtils.getGlobalConfig(this.configuration).getIdentifierGenerator();\n            Object idValue = metaObject.getValue(keyProperty);\n            if (identifierGenerator.assignId(idValue)) {\n                if (idType.getKey() == IdType.ASSIGN_ID.getKey()) {\n                    Number id = identifierGenerator.nextId(entity);\n                    metaObject.setValue(keyProperty, OgnlOps.convertValue(id, tableInfo.getKeyType()));\n                } else if (idType.getKey() == IdType.ASSIGN_UUID.getKey()) {\n                    if(String.class.equals(tableInfo.getKeyType())) {\n                        metaObject.setValue(keyProperty, identifierGenerator.nextUUID(entity));\n                    } else {\n                        log.warn(\"The current ID generation strategy does not support: \" + tableInfo.getKeyType());\n                    }\n                }\n            }\n        }\n    }\n\n    protected void insertFill(MetaObject metaObject, TableInfo tableInfo) {\n        GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> {\n            if (metaObjectHandler.openInsertFill() && metaObjectHandler.openInsertFill(mappedStatement) && tableInfo.isWithInsertFill()) {\n                metaObjectHandler.insertFill(metaObject);\n            }\n        });\n    }\n\n    protected void updateFill(MetaObject metaObject, TableInfo tableInfo) {\n        GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> {\n            if (metaObjectHandler.openUpdateFill() && metaObjectHandler.openUpdateFill(mappedStatement) && tableInfo.isWithUpdateFill()) {\n                metaObjectHandler.updateFill(metaObject);\n            }\n        });\n    }\n\n    /**\n     * 处理正常批量插入逻辑\n     * <p>\n     * org.apache.ibatis.session.defaults.DefaultSqlSession$StrictMap 该类方法\n     * wrapCollection 实现 StrictMap 封装逻辑\n     * </p>\n     *\n     * @return 集合参数\n     * @deprecated 3.5.3.2\n     */\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Deprecated\n    protected Collection<Object> getParameters(Object parameterObject) {\n        Collection<Object> parameters = null;\n        if (parameterObject instanceof Collection) {\n            parameters = (Collection) parameterObject;\n        } else if (ArrayUtils.isArray(parameterObject)) {\n            parameters = toCollection(parameterObject);\n        } else if (parameterObject instanceof Map) {\n            Map parameterMap = (Map) parameterObject;\n            // 约定 coll collection list array 这四个特殊key值处理批量.\n            // 尝试提取参数进行填充，如果是多参数时，在使用注解时，请注意使用collection，list，array进行声明\n            if (parameterMap.containsKey(\"collection\")) {\n                return toCollection(parameterMap.get(\"collection\"));\n            } else if (parameterMap.containsKey(Constants.COLL)) {\n                // 兼容逻辑删除对象填充，这里的集合字段后面重构的时候应该和原生保持一致，使用collection\n                parameters = toCollection(parameterMap.get(Constants.COLL));\n            } else if (parameterMap.containsKey(Constants.LIST)) {\n                parameters = toCollection(parameterMap.get(Constants.LIST));\n            } else if (parameterMap.containsKey(Constants.ARRAY)) {\n                parameters = toCollection(parameterMap.get(Constants.ARRAY));\n            }\n        }\n        return parameters;\n    }\n\n    /**\n     * 提取特殊key值 (只支持外层参数,嵌套参数不考虑)\n     * List<Map>虽然这种写法目前可以进去提取et,但不考虑再提取list等其他类型,只做简单参数提取\n     *\n     * @param parameterObject 参数\n     * @return 预期可能为填充参数值\n     */\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    private Collection<Object> extractParameters(Object parameterObject) {\n        if (parameterObject instanceof Collection) {\n            return (Collection) parameterObject;\n        } else if (ArrayUtils.isArray(parameterObject)) {\n            return toCollection(parameterObject);\n        } else if (parameterObject instanceof Map) {\n            Collection<Object> parameters = new ArrayList<>();\n            Map<String, Object> parameterMap = (Map) parameterObject;\n            Set<Object> objectSet = new HashSet<>();\n            parameterMap.forEach((k, v) -> {\n                if (objectSet.add(v)) {\n                    Collection<Object> collection = toCollection(v);\n                    parameters.addAll(collection);\n                }\n            });\n            return parameters;\n        } else {\n            return Collections.singleton(parameterObject);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected Collection<Object> toCollection(Object value) {\n        if (value == null) {\n            return Collections.emptyList();\n        }\n        if (ArrayUtils.isArray(value) && !value.getClass().getComponentType().isPrimitive()) {\n            return Arrays.asList((Object[]) value);\n        } else if (Collection.class.isAssignableFrom(value.getClass())) {\n            return (Collection<Object>) value;\n        } else {\n            return Collections.singletonList(value);\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisPlusVersion.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.security.CodeSource;\nimport java.util.jar.Attributes;\nimport java.util.jar.JarFile;\n\n/**\n * 获取Mybatis-Plus版本\n *\n * @author zengzhihong\n */\npublic class MybatisPlusVersion {\n\n    private MybatisPlusVersion() {\n    }\n\n    public static String getVersion() {\n        return determineSpringBootVersion();\n    }\n\n    private static String determineSpringBootVersion() {\n        final Package pkg = MybatisPlusVersion.class.getPackage();\n        if (pkg != null && pkg.getImplementationVersion() != null) {\n            return pkg.getImplementationVersion();\n        }\n        CodeSource codeSource = MybatisPlusVersion.class.getProtectionDomain().getCodeSource();\n        if (codeSource == null) {\n            return null;\n        }\n        URL codeSourceLocation = codeSource.getLocation();\n        try {\n            URLConnection connection = codeSourceLocation.openConnection();\n            if (connection instanceof JarURLConnection) {\n                return getImplementationVersion(((JarURLConnection) connection).getJarFile());\n            }\n            try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) {\n                return getImplementationVersion(jarFile);\n            }\n        } catch (Exception ex) {\n            return null;\n        }\n    }\n\n    private static String getImplementationVersion(JarFile jarFile) throws IOException {\n        return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisSqlSessionFactoryBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.SqlRunnerInjector;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.IdWorker;\nimport com.baomidou.mybatisplus.core.toolkit.NetUtils;\nimport org.apache.ibatis.exceptions.ExceptionFactory;\nimport org.apache.ibatis.executor.ErrorContext;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.net.InetAddress;\nimport java.util.Properties;\n\n/**\n * 重写SqlSessionFactoryBuilder\n *\n * @author nieqiurong 2019/2/23.\n */\npublic class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {\n\n    @SuppressWarnings(\"Duplicates\")\n    @Override\n    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {\n        try {\n            MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(reader, environment, properties);\n            return build(parser.parse());\n        } catch (Exception e) {\n            throw ExceptionFactory.wrapException(\"Error building SqlSession.\", e);\n        } finally {\n            ErrorContext.instance().reset();\n            try {\n                reader.close();\n            } catch (IOException e) {\n                // Intentionally ignore. Prefer previous error.\n            }\n        }\n    }\n\n    @SuppressWarnings(\"Duplicates\")\n    @Override\n    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {\n        try {\n            MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties);\n            return build(parser.parse());\n        } catch (Exception e) {\n            throw ExceptionFactory.wrapException(\"Error building SqlSession.\", e);\n        } finally {\n            ErrorContext.instance().reset();\n            try {\n                inputStream.close();\n            } catch (IOException e) {\n                // Intentionally ignore. Prefer previous error.\n            }\n        }\n    }\n\n    @Override\n    public SqlSessionFactory build(Configuration configuration) {\n        GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);\n\n        IdentifierGenerator identifierGenerator;\n        if (null == globalConfig.getIdentifierGenerator()) {\n            GlobalConfig.Sequence sequence = globalConfig.getSequence();\n            if (sequence.getWorkerId() != null && sequence.getDatacenterId() != null) {\n                identifierGenerator = new DefaultIdentifierGenerator(sequence.getWorkerId(), sequence.getDatacenterId());\n            } else {\n                NetUtils.NetProperties netProperties = new NetUtils.NetProperties(sequence.getPreferredNetworks(), sequence.getIgnoredInterfaces());\n                try {\n                    InetAddress inetAddress = new NetUtils(netProperties).findFirstNonLoopbackAddress();\n                    identifierGenerator = new DefaultIdentifierGenerator(inetAddress);\n                } catch (Exception e) {\n                    Log log = LogFactory.getLog(MybatisSqlSessionFactoryBuilder.class);\n                    identifierGenerator = DefaultIdentifierGenerator.getFixedIdentifierGenerator(log);\n                }\n            }\n            globalConfig.setIdentifierGenerator(identifierGenerator);\n        } else {\n            identifierGenerator = globalConfig.getIdentifierGenerator();\n        }\n        IdWorker.setIdentifierGenerator(identifierGenerator);\n\n        if (globalConfig.isEnableSqlRunner()) {\n            new SqlRunnerInjector().inject(configuration);\n        }\n\n        SqlSessionFactory sqlSessionFactory = super.build(configuration);\n\n        // 缓存 sqlSessionFactory\n        globalConfig.setSqlSessionFactory(sqlSessionFactory);\n\n        return sqlSessionFactory;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport org.apache.ibatis.builder.BaseBuilder;\nimport org.apache.ibatis.builder.BuilderException;\nimport org.apache.ibatis.builder.xml.XMLConfigBuilder;\nimport org.apache.ibatis.builder.xml.XMLMapperBuilder;\nimport org.apache.ibatis.builder.xml.XMLMapperEntityResolver;\nimport org.apache.ibatis.datasource.DataSourceFactory;\nimport org.apache.ibatis.executor.ErrorContext;\nimport org.apache.ibatis.executor.loader.ProxyFactory;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.io.VFS;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.parsing.XNode;\nimport org.apache.ibatis.parsing.XPathParser;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.reflection.DefaultReflectorFactory;\nimport org.apache.ibatis.reflection.MetaClass;\nimport org.apache.ibatis.reflection.ReflectorFactory;\nimport org.apache.ibatis.reflection.factory.ObjectFactory;\nimport org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;\nimport org.apache.ibatis.session.*;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.type.JdbcType;\n\nimport javax.sql.DataSource;\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.util.Properties;\n\n/**\n * 从 {@link XMLConfigBuilder} copy 过来, 使用自己的 MybatisConfiguration 而不是 Configuration\n *\n * @author hubin\n * @since 2017-01-04\n */\npublic class MybatisXMLConfigBuilder extends BaseBuilder {\n\n    private boolean parsed;\n    private final XPathParser parser;\n    private String environment;\n    private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();\n\n\n    public MybatisXMLConfigBuilder(Reader reader) {\n        this(reader, null, null);\n    }\n\n    public MybatisXMLConfigBuilder(Reader reader, String environment) {\n        this(reader, environment, null);\n    }\n\n    public MybatisXMLConfigBuilder(Reader reader, String environment, Properties props) {\n        this(MybatisConfiguration.class, reader, environment, props);\n    }\n\n    public MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, Reader reader, String environment,\n                            Properties props) {\n        this(configClass, new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);\n    }\n\n    public MybatisXMLConfigBuilder(InputStream inputStream) {\n        this(inputStream, null, null);\n    }\n\n    public MybatisXMLConfigBuilder(InputStream inputStream, String environment) {\n        this(inputStream, environment, null);\n    }\n\n    public MybatisXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {\n        this(MybatisConfiguration.class, inputStream, environment, props);\n    }\n\n    public MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,\n                            Properties props) {\n        this(configClass, new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);\n    }\n\n    private MybatisXMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,\n                             Properties props) {\n        super(newConfig(configClass));\n        ErrorContext.instance().resource(\"SQL Mapper Configuration\");\n        this.configuration.setVariables(props);\n        this.parsed = false;\n        this.environment = environment;\n        this.parser = parser;\n    }\n\n    public Configuration parse() {\n        if (parsed) {\n            throw new BuilderException(\"Each XMLConfigBuilder can only be used once.\");\n        }\n        parsed = true;\n        parseConfiguration(parser.evalNode(\"/configuration\"));\n        return configuration;\n    }\n\n    private void parseConfiguration(XNode root) {\n        try {\n            // issue #117 read properties first\n            propertiesElement(root.evalNode(\"properties\"));\n            Properties settings = settingsAsProperties(root.evalNode(\"settings\"));\n            loadCustomVfsImpl(settings);\n            loadCustomLogImpl(settings);\n            typeAliasesElement(root.evalNode(\"typeAliases\"));\n            pluginsElement(root.evalNode(\"plugins\"));\n            objectFactoryElement(root.evalNode(\"objectFactory\"));\n            objectWrapperFactoryElement(root.evalNode(\"objectWrapperFactory\"));\n            reflectorFactoryElement(root.evalNode(\"reflectorFactory\"));\n            settingsElement(settings);\n            // read it after objectFactory and objectWrapperFactory issue #631\n            environmentsElement(root.evalNode(\"environments\"));\n            databaseIdProviderElement(root.evalNode(\"databaseIdProvider\"));\n            typeHandlersElement(root.evalNode(\"typeHandlers\"));\n            mappersElement(root.evalNode(\"mappers\"));\n        } catch (Exception e) {\n            throw new BuilderException(\"Error parsing SQL Mapper Configuration. Cause: \" + e, e);\n        }\n    }\n\n    private Properties settingsAsProperties(XNode context) {\n        if (context == null) {\n            return new Properties();\n        }\n        Properties props = context.getChildrenAsProperties();\n        // Check that all settings are known to the configuration class\n        MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);\n        for (Object key : props.keySet()) {\n            if (!metaConfig.hasSetter(String.valueOf(key))) {\n                throw new BuilderException(\n                    \"The setting \" + key + \" is not known.  Make sure you spelled it correctly (case sensitive).\");\n            }\n        }\n        return props;\n    }\n\n    private void loadCustomVfsImpl(Properties props) throws ClassNotFoundException {\n        String value = props.getProperty(\"vfsImpl\");\n        if (value == null) {\n            return;\n        }\n        String[] clazzes = value.split(\",\");\n        for (String clazz : clazzes) {\n            if (!clazz.isEmpty()) {\n                @SuppressWarnings(\"unchecked\")\n                Class<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);\n                configuration.setVfsImpl(vfsImpl);\n            }\n        }\n    }\n\n    private void loadCustomLogImpl(Properties props) {\n        Class<? extends Log> logImpl = resolveClass(props.getProperty(\"logImpl\"));\n        configuration.setLogImpl(logImpl);\n    }\n\n    private void typeAliasesElement(XNode context) {\n        if (context == null) {\n            return;\n        }\n        for (XNode child : context.getChildren()) {\n            if (\"package\".equals(child.getName())) {\n                String typeAliasPackage = child.getStringAttribute(\"name\");\n                configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);\n            } else {\n                String alias = child.getStringAttribute(\"alias\");\n                String type = child.getStringAttribute(\"type\");\n                try {\n                    Class<?> clazz = Resources.classForName(type);\n                    if (alias == null) {\n                        typeAliasRegistry.registerAlias(clazz);\n                    } else {\n                        typeAliasRegistry.registerAlias(alias, clazz);\n                    }\n                } catch (ClassNotFoundException e) {\n                    throw new BuilderException(\"Error registering typeAlias for '\" + alias + \"'. Cause: \" + e, e);\n                }\n            }\n        }\n    }\n\n    private void pluginsElement(XNode context) throws Exception {\n        if (context != null) {\n            for (XNode child : context.getChildren()) {\n                String interceptor = child.getStringAttribute(\"interceptor\");\n                Properties properties = child.getChildrenAsProperties();\n                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor()\n                    .newInstance();\n                interceptorInstance.setProperties(properties);\n                configuration.addInterceptor(interceptorInstance);\n            }\n        }\n    }\n\n    private void objectFactoryElement(XNode context) throws Exception {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\");\n            Properties properties = context.getChildrenAsProperties();\n            ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();\n            factory.setProperties(properties);\n            configuration.setObjectFactory(factory);\n        }\n    }\n\n    private void objectWrapperFactoryElement(XNode context) throws Exception {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\");\n            ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();\n            configuration.setObjectWrapperFactory(factory);\n        }\n    }\n\n    private void reflectorFactoryElement(XNode context) throws Exception {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\");\n            ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();\n            configuration.setReflectorFactory(factory);\n        }\n    }\n\n    private void propertiesElement(XNode context) throws Exception {\n        if (context == null) {\n            return;\n        }\n        Properties defaults = context.getChildrenAsProperties();\n        String resource = context.getStringAttribute(\"resource\");\n        String url = context.getStringAttribute(\"url\");\n        if (resource != null && url != null) {\n            throw new BuilderException(\n                \"The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.\");\n        }\n        if (resource != null) {\n            defaults.putAll(Resources.getResourceAsProperties(resource));\n        } else if (url != null) {\n            defaults.putAll(Resources.getUrlAsProperties(url));\n        }\n        Properties vars = configuration.getVariables();\n        if (vars != null) {\n            defaults.putAll(vars);\n        }\n        parser.setVariables(defaults);\n        configuration.setVariables(defaults);\n    }\n\n    private void settingsElement(Properties props) {\n        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty(\"autoMappingBehavior\", \"PARTIAL\")));\n        configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty(\"autoMappingUnknownColumnBehavior\", \"NONE\")));\n        configuration.setCacheEnabled(booleanValueOf(props.getProperty(\"cacheEnabled\"), true));\n        configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty(\"proxyFactory\")));\n        configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty(\"lazyLoadingEnabled\"), false));\n        configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty(\"aggressiveLazyLoading\"), false));\n        configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty(\"multipleResultSetsEnabled\"), true));\n        configuration.setUseColumnLabel(booleanValueOf(props.getProperty(\"useColumnLabel\"), true));\n        configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty(\"useGeneratedKeys\"), false));\n        configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty(\"defaultExecutorType\", \"SIMPLE\")));\n        configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty(\"defaultStatementTimeout\"), null));\n        configuration.setDefaultFetchSize(integerValueOf(props.getProperty(\"defaultFetchSize\"), null));\n        configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty(\"defaultResultSetType\")));\n        configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty(\"safeRowBoundsEnabled\"), false));\n        configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty(\"localCacheScope\", \"SESSION\")));\n        configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty(\"jdbcTypeForNull\", \"OTHER\")));\n        configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty(\"lazyLoadTriggerMethods\"), \"equals,clone,hashCode,toString\"));\n        configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty(\"safeResultHandlerEnabled\"), true));\n        configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty(\"defaultScriptingLanguage\")));\n        configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty(\"defaultEnumTypeHandler\")));\n        configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty(\"callSettersOnNulls\"), false));\n        configuration.setUseActualParamName(booleanValueOf(props.getProperty(\"useActualParamName\"), true));\n        configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty(\"returnInstanceForEmptyRow\"), false));\n        configuration.setLogPrefix(props.getProperty(\"logPrefix\"));\n        configuration.setConfigurationFactory(resolveClass(props.getProperty(\"configurationFactory\")));\n        configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty(\"shrinkWhitespacesInSql\"), false));\n        configuration.setArgNameBasedConstructorAutoMapping(booleanValueOf(props.getProperty(\"argNameBasedConstructorAutoMapping\"), false));\n        configuration.setDefaultSqlProviderType(resolveClass(props.getProperty(\"defaultSqlProviderType\")));\n        configuration.setNullableOnForEach(booleanValueOf(props.getProperty(\"nullableOnForEach\"), false));\n        // TODO 这里改变了原生行为\n        configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty(\"mapUnderscoreToCamelCase\"), true));\n        ((MybatisConfiguration) configuration).setUseGeneratedShortKey(booleanValueOf(props.getProperty(\"useGeneratedShortKey\"), true));\n    }\n\n    private void environmentsElement(XNode context) throws Exception {\n        if (context == null) {\n            return;\n        }\n        if (environment == null) {\n            environment = context.getStringAttribute(\"default\");\n        }\n        for (XNode child : context.getChildren()) {\n            String id = child.getStringAttribute(\"id\");\n            if (isSpecifiedEnvironment(id)) {\n                TransactionFactory txFactory = transactionManagerElement(child.evalNode(\"transactionManager\"));\n                DataSourceFactory dsFactory = dataSourceElement(child.evalNode(\"dataSource\"));\n                DataSource dataSource = dsFactory.getDataSource();\n                Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)\n                    .dataSource(dataSource);\n                configuration.setEnvironment(environmentBuilder.build());\n                break;\n            }\n        }\n    }\n\n    private void databaseIdProviderElement(XNode context) throws Exception {\n        if (context == null) {\n            return;\n        }\n        String type = context.getStringAttribute(\"type\");\n        // awful patch to keep backward compatibility\n        if (\"VENDOR\".equals(type)) {\n            type = \"DB_VENDOR\";\n        }\n        Properties properties = context.getChildrenAsProperties();\n        DatabaseIdProvider databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor()\n            .newInstance();\n        databaseIdProvider.setProperties(properties);\n        Environment environment = configuration.getEnvironment();\n        if (environment != null) {\n            String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());\n            configuration.setDatabaseId(databaseId);\n        }\n    }\n\n    private TransactionFactory transactionManagerElement(XNode context) throws Exception {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\");\n            Properties props = context.getChildrenAsProperties();\n            TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();\n            factory.setProperties(props);\n            return factory;\n        }\n        throw new BuilderException(\"Environment declaration requires a TransactionFactory.\");\n    }\n\n    private DataSourceFactory dataSourceElement(XNode context) throws Exception {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\");\n            Properties props = context.getChildrenAsProperties();\n            DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();\n            factory.setProperties(props);\n            return factory;\n        }\n        throw new BuilderException(\"Environment declaration requires a DataSourceFactory.\");\n    }\n\n    private void typeHandlersElement(XNode context) {\n        if (context == null) {\n            return;\n        }\n        for (XNode child : context.getChildren()) {\n            if (\"package\".equals(child.getName())) {\n                String typeHandlerPackage = child.getStringAttribute(\"name\");\n                typeHandlerRegistry.register(typeHandlerPackage);\n            } else {\n                String javaTypeName = child.getStringAttribute(\"javaType\");\n                String jdbcTypeName = child.getStringAttribute(\"jdbcType\");\n                String handlerTypeName = child.getStringAttribute(\"handler\");\n                Class<?> javaTypeClass = resolveClass(javaTypeName);\n                JdbcType jdbcType = resolveJdbcType(jdbcTypeName);\n                Class<?> typeHandlerClass = resolveClass(handlerTypeName);\n                if (javaTypeClass != null) {\n                    if (jdbcType == null) {\n                        typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);\n                    } else {\n                        typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);\n                    }\n                } else {\n                    typeHandlerRegistry.register(typeHandlerClass);\n                }\n            }\n        }\n    }\n\n    private void mappersElement(XNode context) throws Exception {\n        if (context == null) {\n            return;\n        }\n        for (XNode child : context.getChildren()) {\n            if (\"package\".equals(child.getName())) {\n                String mapperPackage = child.getStringAttribute(\"name\");\n                configuration.addMappers(mapperPackage);\n            } else {\n                String resource = child.getStringAttribute(\"resource\");\n                String url = child.getStringAttribute(\"url\");\n                String mapperClass = child.getStringAttribute(\"class\");\n                if (resource != null && url == null && mapperClass == null) {\n                    ErrorContext.instance().resource(resource);\n                    try (InputStream inputStream = Resources.getResourceAsStream(resource)) {\n                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,\n                            configuration.getSqlFragments());\n                        mapperParser.parse();\n                    }\n                } else if (resource == null && url != null && mapperClass == null) {\n                    ErrorContext.instance().resource(url);\n                    try (InputStream inputStream = Resources.getUrlAsStream(url)) {\n                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url,\n                            configuration.getSqlFragments());\n                        mapperParser.parse();\n                    }\n                } else if (resource == null && url == null && mapperClass != null) {\n                    Class<?> mapperInterface = Resources.classForName(mapperClass);\n                    configuration.addMapper(mapperInterface);\n                } else {\n                    throw new BuilderException(\n                        \"A mapper element may only specify a url, resource or class, but not more than one.\");\n                }\n            }\n        }\n    }\n\n    private boolean isSpecifiedEnvironment(String id) {\n        if (environment == null) {\n            throw new BuilderException(\"No environment specified.\");\n        }\n        if (id == null) {\n            throw new BuilderException(\"Environment requires an id attribute.\");\n        }\n        return environment.equals(id);\n    }\n\n    private static Configuration newConfig(Class<? extends Configuration> configClass) {\n        try {\n            return configClass.getDeclaredConstructor().newInstance();\n        } catch (Exception ex) {\n            throw new BuilderException(\"Failed to create a new Configuration instance.\", ex);\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLLanguageDriver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;\nimport org.apache.ibatis.builder.IncompleteElementException;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\nimport org.apache.ibatis.parsing.XNode;\nimport org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.List;\n\n/**\n * 继承 {@link XMLLanguageDriver} 重装构造函数, 使用自己的 MybatisParameterHandler\n *\n * @author hubin\n * @since 2016-03-11\n */\npublic class MybatisXMLLanguageDriver extends XMLLanguageDriver {\n\n    @Override\n    public ParameterHandler createParameterHandler(MappedStatement mappedStatement,\n                                                   Object parameterObject, BoundSql boundSql) {\n        // 使用 MybatisParameterHandler 而不是 ParameterHandler\n        return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql);\n    }\n\n    @Override\n    public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {\n        MybatisXMLScriptBuilder builder = new MybatisXMLScriptBuilder(configuration, script, parameterType);\n        return builder.parseScriptNode();\n    }\n\n    @Override\n    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {\n        GlobalConfig.DbConfig config = GlobalConfigUtils.getDbConfig(configuration);\n        if (config.isReplacePlaceholder()) {\n            List<String> find = SqlUtils.findPlaceholder(script);\n            if (CollectionUtils.isNotEmpty(find)) {\n                try {\n                    script = SqlUtils.replaceSqlPlaceholder(script, find, config.getEscapeSymbol());\n                } catch (MybatisPlusException e) {\n                    throw new IncompleteElementException(e);\n                }\n            }\n        }\n        return super.createSqlSource(configuration, script, parameterType);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLMapperBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport org.apache.ibatis.builder.BaseBuilder;\nimport org.apache.ibatis.builder.BuilderException;\nimport org.apache.ibatis.builder.CacheRefResolver;\nimport org.apache.ibatis.builder.IncompleteElementException;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.builder.ResultMapResolver;\nimport org.apache.ibatis.builder.xml.XMLMapperEntityResolver;\nimport org.apache.ibatis.builder.xml.XMLStatementBuilder;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.executor.ErrorContext;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.mapping.Discriminator;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.ParameterMode;\nimport org.apache.ibatis.mapping.ResultFlag;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.mapping.ResultMapping;\nimport org.apache.ibatis.parsing.XNode;\nimport org.apache.ibatis.parsing.XPathParser;\nimport org.apache.ibatis.reflection.MetaClass;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * 重写了{@link org.apache.ibatis.builder.xml.XMLMapperBuilder} 替换了 {@link org.apache.ibatis.builder.MapperBuilderAssistant}\n *\n * @author nieqiurong\n * @since 3.5.6\n */\npublic class MybatisXMLMapperBuilder extends BaseBuilder {\n\n    private final XPathParser parser;\n    private final MapperBuilderAssistant builderAssistant;\n    private final Map<String, XNode> sqlFragments;\n    private final String resource;\n\n    @Deprecated\n    public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments,\n                                   String namespace) {\n        this(reader, configuration, resource, sqlFragments);\n        this.builderAssistant.setCurrentNamespace(namespace);\n    }\n\n    @Deprecated\n    public MybatisXMLMapperBuilder(Reader reader, Configuration configuration, String resource,\n                                   Map<String, XNode> sqlFragments) {\n        this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration,\n            resource, sqlFragments);\n    }\n\n    public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,\n                                   Map<String, XNode> sqlFragments, String namespace) {\n        this(inputStream, configuration, resource, sqlFragments);\n        this.builderAssistant.setCurrentNamespace(namespace);\n    }\n\n    public MybatisXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource,\n                                   Map<String, XNode> sqlFragments) {\n        this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration,\n            resource, sqlFragments);\n    }\n\n    private MybatisXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource,\n                                    Map<String, XNode> sqlFragments) {\n        super(configuration);\n        this.builderAssistant = new MybatisMapperBuilderAssistant(configuration, resource);\n        this.parser = parser;\n        this.sqlFragments = sqlFragments;\n        this.resource = resource;\n    }\n\n    public void parse() {\n        if (!configuration.isResourceLoaded(resource)) {\n            configurationElement(parser.evalNode(\"/mapper\"));\n            configuration.addLoadedResource(resource);\n            bindMapperForNamespace();\n        }\n        configuration.parsePendingResultMaps(false);\n        configuration.parsePendingCacheRefs(false);\n        configuration.parsePendingStatements(false);\n    }\n\n    public XNode getSqlFragment(String refid) {\n        return sqlFragments.get(refid);\n    }\n\n    private void configurationElement(XNode context) {\n        try {\n            String namespace = context.getStringAttribute(\"namespace\");\n            if (namespace == null || namespace.isEmpty()) {\n                throw new BuilderException(\"Mapper's namespace cannot be empty\");\n            }\n            builderAssistant.setCurrentNamespace(namespace);\n            cacheRefElement(context.evalNode(\"cache-ref\"));\n            cacheElement(context.evalNode(\"cache\"));\n            parameterMapElement(context.evalNodes(\"/mapper/parameterMap\"));\n            resultMapElements(context.evalNodes(\"/mapper/resultMap\"));\n            sqlElement(context.evalNodes(\"/mapper/sql\"));\n            buildStatementFromContext(context.evalNodes(\"select|insert|update|delete\"));\n        } catch (Exception e) {\n            throw new BuilderException(\"Error parsing Mapper XML. The XML location is '\" + resource + \"'. Cause: \" + e, e);\n        }\n    }\n\n    private void buildStatementFromContext(List<XNode> list) {\n        if (configuration.getDatabaseId() != null) {\n            buildStatementFromContext(list, configuration.getDatabaseId());\n        }\n        buildStatementFromContext(list, null);\n    }\n\n    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {\n        for (XNode context : list) {\n            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context,\n                requiredDatabaseId);\n            try {\n                statementParser.parseStatementNode();\n            } catch (IncompleteElementException e) {\n                configuration.addIncompleteStatement(statementParser);\n            }\n        }\n    }\n\n    private void cacheRefElement(XNode context) {\n        if (context != null) {\n            configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute(\"namespace\"));\n            CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant,\n                context.getStringAttribute(\"namespace\"));\n            try {\n                cacheRefResolver.resolveCacheRef();\n            } catch (IncompleteElementException e) {\n                configuration.addIncompleteCacheRef(cacheRefResolver);\n            }\n        }\n    }\n\n    private void cacheElement(XNode context) {\n        if (context != null) {\n            String type = context.getStringAttribute(\"type\", \"PERPETUAL\");\n            Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);\n            String eviction = context.getStringAttribute(\"eviction\", \"LRU\");\n            Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);\n            Long flushInterval = context.getLongAttribute(\"flushInterval\");\n            Integer size = context.getIntAttribute(\"size\");\n            boolean readWrite = !context.getBooleanAttribute(\"readOnly\", false);\n            boolean blocking = context.getBooleanAttribute(\"blocking\", false);\n            Properties props = context.getChildrenAsProperties();\n            builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);\n        }\n    }\n\n    private void parameterMapElement(List<XNode> list) {\n        for (XNode parameterMapNode : list) {\n            String id = parameterMapNode.getStringAttribute(\"id\");\n            String type = parameterMapNode.getStringAttribute(\"type\");\n            Class<?> parameterClass = resolveClass(type);\n            List<XNode> parameterNodes = parameterMapNode.evalNodes(\"parameter\");\n            List<ParameterMapping> parameterMappings = new ArrayList<>();\n            for (XNode parameterNode : parameterNodes) {\n                String property = parameterNode.getStringAttribute(\"property\");\n                String javaType = parameterNode.getStringAttribute(\"javaType\");\n                String jdbcType = parameterNode.getStringAttribute(\"jdbcType\");\n                String resultMap = parameterNode.getStringAttribute(\"resultMap\");\n                String mode = parameterNode.getStringAttribute(\"mode\");\n                String typeHandler = parameterNode.getStringAttribute(\"typeHandler\");\n                Integer numericScale = parameterNode.getIntAttribute(\"numericScale\");\n                ParameterMode modeEnum = resolveParameterMode(mode);\n                Class<?> javaTypeClass = resolveClass(javaType);\n                JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);\n                Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);\n                ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property,\n                    javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);\n                parameterMappings.add(parameterMapping);\n            }\n            builderAssistant.addParameterMap(id, parameterClass, parameterMappings);\n        }\n    }\n\n    private void resultMapElements(List<XNode> list) {\n        for (XNode resultMapNode : list) {\n            try {\n                resultMapElement(resultMapNode);\n            } catch (IncompleteElementException e) {\n                // ignore, it will be retried\n            }\n        }\n    }\n\n    private ResultMap resultMapElement(XNode resultMapNode) {\n        return resultMapElement(resultMapNode, Collections.emptyList(), null);\n    }\n\n    private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings,\n                                       Class<?> enclosingType) {\n        ErrorContext.instance().activity(\"processing \" + resultMapNode.getValueBasedIdentifier());\n        String type = resultMapNode.getStringAttribute(\"type\", resultMapNode.getStringAttribute(\"ofType\",\n            resultMapNode.getStringAttribute(\"resultType\", resultMapNode.getStringAttribute(\"javaType\"))));\n        Class<?> typeClass = resolveClass(type);\n        if (typeClass == null) {\n            typeClass = inheritEnclosingType(resultMapNode, enclosingType);\n        }\n        Discriminator discriminator = null;\n        List<ResultMapping> resultMappings = new ArrayList<>(additionalResultMappings);\n        List<XNode> resultChildren = resultMapNode.getChildren();\n        for (XNode resultChild : resultChildren) {\n            if (\"constructor\".equals(resultChild.getName())) {\n                processConstructorElement(resultChild, typeClass, resultMappings);\n            } else if (\"discriminator\".equals(resultChild.getName())) {\n                discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);\n            } else {\n                List<ResultFlag> flags = new ArrayList<>();\n                if (\"id\".equals(resultChild.getName())) {\n                    flags.add(ResultFlag.ID);\n                }\n                resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));\n            }\n        }\n        String id = resultMapNode.getStringAttribute(\"id\", resultMapNode.getValueBasedIdentifier());\n        String extend = resultMapNode.getStringAttribute(\"extends\");\n        Boolean autoMapping = resultMapNode.getBooleanAttribute(\"autoMapping\");\n        ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator,\n            resultMappings, autoMapping);\n        try {\n            return resultMapResolver.resolve();\n        } catch (IncompleteElementException e) {\n            configuration.addIncompleteResultMap(resultMapResolver);\n            throw e;\n        }\n    }\n\n    protected Class<?> inheritEnclosingType(XNode resultMapNode, Class<?> enclosingType) {\n        if (\"association\".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute(\"resultMap\") == null) {\n            String property = resultMapNode.getStringAttribute(\"property\");\n            if (property != null && enclosingType != null) {\n                MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());\n                return metaResultType.getSetterType(property);\n            }\n        } else if (\"case\".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute(\"resultMap\") == null) {\n            return enclosingType;\n        }\n        return null;\n    }\n\n    private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) {\n        List<XNode> argChildren = resultChild.getChildren();\n        for (XNode argChild : argChildren) {\n            List<ResultFlag> flags = new ArrayList<>();\n            flags.add(ResultFlag.CONSTRUCTOR);\n            if (\"idArg\".equals(argChild.getName())) {\n                flags.add(ResultFlag.ID);\n            }\n            resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));\n        }\n    }\n\n    private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType,\n                                                      List<ResultMapping> resultMappings) {\n        String column = context.getStringAttribute(\"column\");\n        String javaType = context.getStringAttribute(\"javaType\");\n        String jdbcType = context.getStringAttribute(\"jdbcType\");\n        String typeHandler = context.getStringAttribute(\"typeHandler\");\n        Class<?> javaTypeClass = resolveClass(javaType);\n        Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);\n        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);\n        Map<String, String> discriminatorMap = new HashMap<>();\n        for (XNode caseChild : context.getChildren()) {\n            String value = caseChild.getStringAttribute(\"value\");\n            String resultMap = caseChild.getStringAttribute(\"resultMap\",\n                processNestedResultMappings(caseChild, resultMappings, resultType));\n            discriminatorMap.put(value, resultMap);\n        }\n        return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass,\n            discriminatorMap);\n    }\n\n    private void sqlElement(List<XNode> list) {\n        if (configuration.getDatabaseId() != null) {\n            sqlElement(list, configuration.getDatabaseId());\n        }\n        sqlElement(list, null);\n    }\n\n    private void sqlElement(List<XNode> list, String requiredDatabaseId) {\n        for (XNode context : list) {\n            String databaseId = context.getStringAttribute(\"databaseId\");\n            String id = context.getStringAttribute(\"id\");\n            id = builderAssistant.applyCurrentNamespace(id, false);\n            if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {\n                sqlFragments.put(id, context);\n            }\n        }\n    }\n\n    private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {\n        if (requiredDatabaseId != null) {\n            return requiredDatabaseId.equals(databaseId);\n        }\n        if (databaseId != null) {\n            return false;\n        }\n        if (!this.sqlFragments.containsKey(id)) {\n            return true;\n        }\n        // skip this fragment if there is a previous one with a not null databaseId\n        XNode context = this.sqlFragments.get(id);\n        return context.getStringAttribute(\"databaseId\") == null;\n    }\n\n    private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) {\n        String property;\n        if (flags.contains(ResultFlag.CONSTRUCTOR)) {\n            property = context.getStringAttribute(\"name\");\n        } else {\n            property = context.getStringAttribute(\"property\");\n        }\n        String column = context.getStringAttribute(\"column\");\n        String javaType = context.getStringAttribute(\"javaType\");\n        String jdbcType = context.getStringAttribute(\"jdbcType\");\n        String nestedSelect = context.getStringAttribute(\"select\");\n        String nestedResultMap = context.getStringAttribute(\"resultMap\",\n            () -> processNestedResultMappings(context, Collections.emptyList(), resultType));\n        String notNullColumn = context.getStringAttribute(\"notNullColumn\");\n        String columnPrefix = context.getStringAttribute(\"columnPrefix\");\n        String typeHandler = context.getStringAttribute(\"typeHandler\");\n        String resultSet = context.getStringAttribute(\"resultSet\");\n        String foreignColumn = context.getStringAttribute(\"foreignColumn\");\n        boolean lazy = \"lazy\"\n            .equals(context.getStringAttribute(\"fetchType\", configuration.isLazyLoadingEnabled() ? \"lazy\" : \"eager\"));\n        Class<?> javaTypeClass = resolveClass(javaType);\n        Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);\n        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);\n        return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect,\n            nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);\n    }\n\n    private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings,\n                                               Class<?> enclosingType) {\n        if (Arrays.asList(\"association\", \"collection\", \"case\").contains(context.getName())\n            && context.getStringAttribute(\"select\") == null) {\n            validateCollection(context, enclosingType);\n            ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);\n            return resultMap.getId();\n        }\n        return null;\n    }\n\n    protected void validateCollection(XNode context, Class<?> enclosingType) {\n        if (\"collection\".equals(context.getName()) && context.getStringAttribute(\"resultMap\") == null\n            && context.getStringAttribute(\"javaType\") == null) {\n            MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());\n            String property = context.getStringAttribute(\"property\");\n            if (!metaResultType.hasSetter(property)) {\n                throw new BuilderException(\n                    \"Ambiguous collection type for property '\" + property + \"'. You must specify 'javaType' or 'resultMap'.\");\n            }\n        }\n    }\n\n    private void bindMapperForNamespace() {\n        String namespace = builderAssistant.getCurrentNamespace();\n        if (namespace != null) {\n            Class<?> boundType = null;\n            try {\n                boundType = Resources.classForName(namespace);\n            } catch (ClassNotFoundException e) {\n                // ignore, bound type is not required\n            }\n            if (boundType != null && !configuration.hasMapper(boundType)) {\n                // Spring may not know the real resource name so we set a flag\n                // to prevent loading again this resource from the mapper interface\n                // look at MapperAnnotationBuilder#loadXmlResource\n                configuration.addLoadedResource(\"namespace:\" + namespace);\n                configuration.addMapper(boundType);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisXMLScriptBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.builder.BaseBuilder;\nimport org.apache.ibatis.builder.BuilderException;\nimport org.apache.ibatis.mapping.SqlSource;\nimport org.apache.ibatis.parsing.XNode;\nimport org.apache.ibatis.scripting.defaults.RawSqlSource;\nimport org.apache.ibatis.scripting.xmltags.ChooseSqlNode;\nimport org.apache.ibatis.scripting.xmltags.DynamicSqlSource;\nimport org.apache.ibatis.scripting.xmltags.ForEachSqlNode;\nimport org.apache.ibatis.scripting.xmltags.IfSqlNode;\nimport org.apache.ibatis.scripting.xmltags.MixedSqlNode;\nimport org.apache.ibatis.scripting.xmltags.SetSqlNode;\nimport org.apache.ibatis.scripting.xmltags.SqlNode;\nimport org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;\nimport org.apache.ibatis.scripting.xmltags.TextSqlNode;\nimport org.apache.ibatis.scripting.xmltags.TrimSqlNode;\nimport org.apache.ibatis.scripting.xmltags.VarDeclSqlNode;\nimport org.apache.ibatis.scripting.xmltags.WhereSqlNode;\nimport org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;\nimport org.apache.ibatis.session.Configuration;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.WeakHashMap;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * <p>试验性功能,解决mybatis堆内存过大的问题(看后期mybatis官方会不会解决堆内存占用问题)</p>\n * <p>由于大量重复sql节点,导致堆内存过大(本质上属于string导致的堆内存增大问题)</p>\n * <p>例如: {@code <if test=\"createTime!=null\">create_time=#{createTime}</if>}等公共字段</p>\n * <p>\n * 解决方案:  将生成的xml节点值写入字符串常量池,减少后面重复字符串导致的问题\n * <li>\n * 方案一: 缓存一些特定的mybatis-plus生成的占位符与表达式和项目公共字段(改动有点多,需要增加一些特定xml属性来标记是mybatis-plus生成的节点,减少堆内存较少)\n * </li>\n * <li>\n * 方案二: 直接将节点内容intern写入至字符串常量池(改动少,减少堆内存多,弊端可能会将一些无重复的字符串写入至常量池)\n * </li>\n * <li>\n * 方案三: 模拟字符串常量池,减少重复字符串写入至堆内存(代码相对来说不好看点)\n * </li>\n * </p>\n *\n * @author nieqiurong\n * @see XMLScriptBuilder\n * @since 3.5.3.2\n */\npublic class MybatisXMLScriptBuilder extends BaseBuilder {\n\n    private final XNode context;\n    private boolean isDynamic;\n    private final Class<?> parameterType;\n    private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();\n    private static final Map<String, WeakReference<String>> CACHE_STRING = new WeakHashMap<>();\n\n    public MybatisXMLScriptBuilder(Configuration configuration, XNode context) {\n        this(configuration, context, null);\n    }\n\n    public MybatisXMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {\n        super(configuration);\n        this.context = context;\n        this.parameterType = parameterType;\n        initNodeHandlerMap();\n    }\n\n    private void initNodeHandlerMap() {\n        nodeHandlerMap.put(\"trim\", new TrimHandler());\n        nodeHandlerMap.put(\"where\", new WhereHandler());\n        nodeHandlerMap.put(\"set\", new SetHandler());\n        nodeHandlerMap.put(\"foreach\", new ForEachHandler());\n        nodeHandlerMap.put(\"if\", new IfHandler());\n        nodeHandlerMap.put(\"choose\", new ChooseHandler());\n        nodeHandlerMap.put(\"when\", new IfHandler());\n        nodeHandlerMap.put(\"otherwise\", new OtherwiseHandler());\n        nodeHandlerMap.put(\"bind\", new BindHandler());\n    }\n\n    public SqlSource parseScriptNode() {\n        MixedSqlNode rootSqlNode = parseDynamicTags(context);\n        SqlSource sqlSource;\n        if (isDynamic) {\n            sqlSource = new DynamicSqlSource(configuration, rootSqlNode);\n        } else {\n            sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);\n        }\n        return sqlSource;\n    }\n\n    /**\n     * 也可以将XNode节点包裹增强一下,来减少方法的引用,但需要创建对象,这里就直接将每个地方手动改一下了.\n     */\n    private synchronized static String cacheStr(String str) {\n        if (str == null) {\n            return null;\n        }\n        String value = CACHE_STRING.computeIfAbsent(str, WeakReference::new).get();\n        //增强安全处理一下,如果实在是GC处理掉了(可能性小),就返回原来.\n        if (StringUtils.isNotBlank(value)) {\n            return value;\n        }\n        return str;\n    }\n\n    private static final StaticTextSqlNode SPACE_SQL_NODE = new StaticTextSqlNode(StringPool.SPACE);\n\n    private static final Pattern PATTERN = Pattern.compile(\"^\\\\s+|\\\\s+$\");\n\n    private static final Map<String, StaticTextSqlNode> CACHE_EMPTY_SQL_NODE = new ConcurrentHashMap<>();\n\n    /**\n     * 将前后空白符替换成空格\n     *\n     * @param str 字符串 (非空)\n     * @return 处理后文本\n     * @since 3.5.10.1\n     */\n    public static String replaceLeadingAndTrailingWhitespace(String str) {\n        Matcher matcher = PATTERN.matcher(str);\n        return matcher.replaceAll(StringPool.SPACE);\n    }\n\n    protected MixedSqlNode parseDynamicTags(XNode node) {\n        List<SqlNode> contents = new ArrayList<>();\n        NodeList children = node.getNode().getChildNodes();\n        for (int i = 0; i < children.getLength(); i++) {\n            XNode child = node.newXNode(children.item(i));\n            if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {\n                String text = cacheStr(child.getStringBody(\"\"));\n                if (text.trim().isEmpty()) {\n                    StaticTextSqlNode staticTextSqlNode = CACHE_EMPTY_SQL_NODE.computeIfAbsent(text, StaticTextSqlNode::new);\n                    contents.add(staticTextSqlNode);\n                    continue;\n                }\n                TextSqlNode textSqlNode = new TextSqlNode(text);\n                if (textSqlNode.isDynamic()) {\n                    contents.add(textSqlNode);\n                    isDynamic = true;\n                } else {\n                    contents.add(new StaticTextSqlNode(text));\n                }\n            } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628\n                String nodeName = child.getNode().getNodeName();\n                NodeHandler handler = nodeHandlerMap.get(nodeName);\n                if (handler == null) {\n                    throw new BuilderException(\"Unknown element <\" + nodeName + \"> in SQL statement.\");\n                }\n                handler.handleNode(child, contents);\n                isDynamic = true;\n            }\n        }\n        return new MixedSqlNode(contents);\n    }\n\n    private interface NodeHandler {\n        void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);\n    }\n\n    private class BindHandler implements NodeHandler {\n        public BindHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            final String name = cacheStr(nodeToHandle.getStringAttribute(\"name\"));\n            final String expression = cacheStr(nodeToHandle.getStringAttribute(\"value\"));\n            final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);\n            targetContents.add(node);\n        }\n    }\n\n    private class TrimHandler implements NodeHandler {\n        public TrimHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            String prefix = cacheStr(nodeToHandle.getStringAttribute(\"prefix\"));\n            String prefixOverrides = cacheStr(nodeToHandle.getStringAttribute(\"prefixOverrides\"));\n            String suffix = cacheStr(nodeToHandle.getStringAttribute(\"suffix\"));\n            String suffixOverrides = cacheStr(nodeToHandle.getStringAttribute(\"suffixOverrides\"));\n            TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);\n            targetContents.add(trim);\n        }\n    }\n\n    private class WhereHandler implements NodeHandler {\n        public WhereHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);\n            targetContents.add(where);\n        }\n    }\n\n    private class SetHandler implements NodeHandler {\n        public SetHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);\n            targetContents.add(set);\n        }\n    }\n\n    private class ForEachHandler implements NodeHandler {\n        public ForEachHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            String collection = cacheStr(nodeToHandle.getStringAttribute(\"collection\"));\n            Boolean nullable = nodeToHandle.getBooleanAttribute(\"nullable\");\n            String item = cacheStr(nodeToHandle.getStringAttribute(\"item\"));\n            String index = cacheStr(nodeToHandle.getStringAttribute(\"index\"));\n            String open = cacheStr(nodeToHandle.getStringAttribute(\"open\"));\n            String close = cacheStr(nodeToHandle.getStringAttribute(\"close\"));\n            String separator = cacheStr(nodeToHandle.getStringAttribute(\"separator\"));\n            ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, nullable, index, item, open, close, separator);\n            targetContents.add(forEachSqlNode);\n        }\n    }\n\n    private class IfHandler implements NodeHandler {\n        public IfHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            String test = cacheStr(nodeToHandle.getStringAttribute(\"test\"));\n            IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);\n            targetContents.add(ifSqlNode);\n        }\n    }\n\n    private class OtherwiseHandler implements NodeHandler {\n        public OtherwiseHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);\n            targetContents.add(mixedSqlNode);\n        }\n    }\n\n    private class ChooseHandler implements NodeHandler {\n        public ChooseHandler() {\n            // Prevent Synthetic Access\n        }\n\n        @Override\n        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {\n            List<SqlNode> whenSqlNodes = new ArrayList<>();\n            List<SqlNode> otherwiseSqlNodes = new ArrayList<>();\n            handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);\n            SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);\n            ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);\n            targetContents.add(chooseSqlNode);\n        }\n\n        private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {\n            List<XNode> children = chooseSqlNode.getChildren();\n            for (XNode child : children) {\n                String nodeName = child.getNode().getNodeName();\n                NodeHandler handler = nodeHandlerMap.get(nodeName);\n                if (handler instanceof IfHandler) {\n                    handler.handleNode(child, ifSqlNodes);\n                } else if (handler instanceof OtherwiseHandler) {\n                    handler.handleNode(child, defaultSqlNodes);\n                }\n            }\n        }\n\n        private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {\n            SqlNode defaultSqlNode = null;\n            if (defaultSqlNodes.size() == 1) {\n                defaultSqlNode = defaultSqlNodes.get(0);\n            } else if (defaultSqlNodes.size() > 1) {\n                throw new BuilderException(\"Too many default (otherwise) elements in choose statement.\");\n            }\n            return defaultSqlNode;\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/AbstractSqlRunner.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.assist;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport org.apache.ibatis.parsing.GenericTokenParser;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.DOT;\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.HASH_LEFT_BRACE;\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.LEFT_BRACE;\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.LEFT_SQ_BRACKET;\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.RIGHT_BRACE;\nimport static com.baomidou.mybatisplus.core.toolkit.StringPool.RIGHT_SQ_BRACKET;\n\n/**\n * @author nieqiurong\n * @since 3.5.12\n */\npublic abstract class AbstractSqlRunner implements ISqlRunner {\n\n    /**\n     * 默认分词处理器\n     *\n     * @since 3.5.12\n     */\n    private static final GenericTokenParser DEFAULT_TOKEN_PARSER = new GenericTokenParser(LEFT_BRACE, RIGHT_BRACE, content -> HASH_LEFT_BRACE + content + RIGHT_BRACE);\n\n    /**\n     * 校验索引正则\n     *\n     * @since 3.5.12\n     */\n    private static final Pattern INDEX_PATTERN = Pattern.compile(\"^\\\\d+$\");\n\n    /**\n     * 第一个值参数key\n     *\n     * @since 3.5.12\n     */\n    private static final String ARG0 = \"arg0\";\n\n\n    /**\n     * 获取执行语句 (将原始占位符语句转换为标准占位符语句)\n     *\n     * @param sql  原始sql\n     * @param args 参数\n     * @return 执行语句 (带参数占位符)\n     * @since 3.5.12\n     */\n    protected String parse(String sql, Object... args) {\n        if (args != null && args.length == 1) {\n            Object arg = args[0];\n            Class<?> clazz = arg.getClass();\n            return new GenericTokenParser(LEFT_BRACE, RIGHT_BRACE, content -> {\n                if (INDEX_PATTERN.matcher(content).matches()) {\n                    if (arg instanceof Collection || clazz.isArray()) {\n                        return HASH_LEFT_BRACE + ARG0 + LEFT_SQ_BRACKET + content + RIGHT_SQ_BRACKET + RIGHT_BRACE;\n                    }\n                    return HASH_LEFT_BRACE + content + RIGHT_BRACE;\n                } else {\n                    return HASH_LEFT_BRACE + ARG0 + DOT + content + RIGHT_BRACE;\n                }\n            }).parse(sql);\n        }\n        return DEFAULT_TOKEN_PARSER.parse(sql);\n    }\n\n    /**\n     * 获取参数列表\n     *\n     * @param args 参数(单参数时,支持使用Map,List,Array,JavaBean访问)\n     * @return 参数map\n     * @since 3.5.12\n     */\n    protected Map<String, Object> getParams(Object... args) {\n        if (args != null && args.length > 0) {\n            Map<String, Object> params = CollectionUtils.newHashMapWithExpectedSize(args.length);\n            for (int i = 0; i < args.length; i++) {\n                Object arg = args[i];\n                if (i == 0) {\n                    params.put(ARG0, arg);\n                }\n                params.put(String.valueOf(i), arg);\n            }\n            return params;\n        }\n        return new HashMap<>();\n    }\n\n    /**\n     * 获取sqlMap参数\n     * <p>\n     * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean)\n     * <li>当参数为 Map 时可通过{key}进行属性访问\n     * <li>当参数为 JavaBean 时可通过{property}进行属性访问\n     * <li>当参数为 List 时直接访问索引 {0} </li>\n     * <li>当参数为 Array 时直接访问索引 {0} </li>\n     * </p>\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 参数集合\n     */\n    protected Map<String, Object> sqlMap(String sql, Object... args) {\n        Map<String, Object> sqlMap = getParams(args);\n        sqlMap.put(SQL, parse(sql, args));\n        return sqlMap;\n    }\n\n    /**\n     * <p>\n     * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean)\n     * <li>当参数为 Map 时可通过{key}进行属性访问\n     * <li>当参数为 JavaBean 时可通过{property}进行属性访问\n     * <li>当参数为 List 时直接访问索引 {0} </li>\n     * <li>当参数为 Array 时直接访问索引 {0} </li>\n     * </p>\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param page 分页模型\n     * @param args 参数\n     * @return 参数集合\n     */\n    protected Map<String, Object> sqlMap(String sql, IPage<?> page, Object... args) {\n        Map<String, Object> sqlMap = getParams(args);\n        sqlMap.put(PAGE, page);\n        sqlMap.put(SQL, parse(sql, args));\n        return sqlMap;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/ISqlRunner.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.assist;\n\nimport com.baomidou.mybatisplus.core.injector.SqlRunnerInjector;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * SqlRunner执行接口\n * <p>\n * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean)\n * <li>当参数为 Map 时可通过{key}进行属性访问\n * <li>当参数为 JavaBean 时可通过{property}进行属性访问\n * <li>当参数为 List 时直接访问索引 {0} </li>\n * <li>当参数为 Array 时直接访问索引 {0} </li>\n * </p>\n *\n * @author yuxiaobin, nieqiurong\n * @since 2018/2/7\n */\npublic interface ISqlRunner {\n\n    /**\n     * INSERT 语句\n     */\n    String INSERT = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.Insert\";\n\n    /**\n     * DELETE 语句\n     */\n    String DELETE = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.Delete\";\n\n    /**\n     * UPDATE 语句\n     */\n    String UPDATE = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.Update\";\n\n    /**\n     * SELECT_LIST 语句\n     */\n    String SELECT_LIST = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.SelectList\";\n\n    /**\n     * SELECT_OBJS 语句\n     */\n    String SELECT_OBJS = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.SelectObjs\";\n\n    /**\n     * COUNT 语句\n     */\n    String COUNT = \"com.baomidou.mybatisplus.core.mapper.SqlRunner.Count\";\n\n    /**\n     * 注入SQL脚本\n     *\n     * @deprecated 3.5.12 {@link SqlRunnerInjector#SQL_SCRIPT}\n     */\n    @Deprecated\n    String SQL_SCRIPT = \"${sql}\";\n\n    /**\n     * sql访问参数\n     */\n    String SQL = \"sql\";\n\n    /**\n     * page访问参数\n     */\n    String PAGE = \"page\";\n\n    /**\n     * 执行插入语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 插入结果\n     */\n    boolean insert(String sql, Object... args);\n\n    /**\n     * 执行删除语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 删除结果\n     */\n    boolean delete(String sql, Object... args);\n\n    /**\n     * 执行更新语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 更新结果\n     */\n    boolean update(String sql, Object... args);\n\n    /**\n     * 根据sql查询Map结果集\n     * <p>SqlRunner.db().selectList(\"select * from tbl_user where name={0}\", \"Caratacus\")</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数列表\n     * @return 结果集\n     */\n    List<Map<String, Object>> selectList(String sql, Object... args);\n\n    /**\n     * 根据sql查询一个字段值的结果集\n     * <p>注意：该方法只会返回一个字段的值， 如果需要多字段，请参考{@link #selectList(String, Object...)}</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 结果集\n     */\n    List<Object> selectObjs(String sql, Object... args);\n\n    /**\n     * 根据sql查询一个字段值的一条结果\n     * <p>注意：该方法只会返回一个字段的值， 如果需要多字段，请参考{@link #selectOne(String, Object...)}</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 结果\n     */\n    Object selectObj(String sql, Object... args);\n\n    /**\n     * 查询总数\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 总记录数\n     */\n    long selectCount(String sql, Object... args);\n\n    /**\n     * 获取单条记录\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 单行结果集 (当执行语句返回多条记录时,只会选取第一条记录)\n     */\n    Map<String, Object> selectOne(String sql, Object... args);\n\n    /**\n     * 分页查询\n     *\n     * @param page 分页对象\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @param <E>  E\n     * @return 分页数据\n     */\n    <E extends IPage<Map<String, Object>>> E selectPage(E page, String sql, Object... args);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/assist/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 增强辅助相关\n */\npackage com.baomidou.mybatisplus.core.assist;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/BatchMethod.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.batch;\n\nimport org.apache.ibatis.mapping.MappedStatement;\n\n/**\n * 批量执行方法\n *\n * @author nieqiurong\n * @since 3.5.4\n */\npublic class BatchMethod<T> {\n\n    /**\n     * 执行的{@link MappedStatement#getId()}\n     */\n    private final String statementId;\n\n    /**\n     * 方法参数转换器,默认传递批量的entity的参数\n     */\n    private ParameterConvert<T> parameterConvert = (entity) -> entity;\n\n    public BatchMethod(String statementId) {\n        this.statementId = statementId;\n    }\n\n    public BatchMethod(String statementId, ParameterConvert<T> parameterConvert) {\n        this.statementId = statementId;\n        this.parameterConvert = parameterConvert;\n    }\n\n    public String getStatementId() {\n        return statementId;\n    }\n\n    public ParameterConvert<T> getParameterConvert() {\n        return parameterConvert;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/BatchSqlSession.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.batch;\n\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.session.RowBounds;\nimport org.apache.ibatis.session.SqlSession;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 当使用Batch混合查询时,每次都会将原来的结果集清空,建议使用Batch时就不要混合使用select了 (后面看看要不要改成动态代理把...)\n *\n * @author nieqiurong\n * @since 3.5.4\n */\npublic class BatchSqlSession {\n\n    private final SqlSession sqlSession;\n\n    private final List<BatchResult> resultBatchList = new ArrayList<>();\n\n    public BatchSqlSession(SqlSession sqlSession) {\n        this.sqlSession = sqlSession;\n    }\n\n    public <T> T selectOne(String statement) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectOne(statement);\n    }\n\n    public <T> T selectOne(String statement, Object parameter) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectOne(statement, parameter);\n    }\n\n    public <E> List<E> selectList(String statement) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectList(statement);\n    }\n\n    public <E> List<E> selectList(String statement, Object parameter) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectList(statement, parameter);\n    }\n\n    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectList(statement, parameter, rowBounds);\n    }\n\n    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectMap(statement, mapKey);\n    }\n\n    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectMap(statement, parameter, mapKey);\n    }\n\n    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {\n        resultBatchList.addAll(sqlSession.flushStatements());\n        return sqlSession.selectMap(statement, parameter, mapKey, rowBounds);\n    }\n\n    public List<BatchResult> getResultBatchList() {\n        return resultBatchList;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/MybatisBatch.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.batch;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiPredicate;\nimport java.util.function.Function;\n\n/**\n * <li>事务需要自行控制</li>\n * <li>批次数据尽量自行切割处理</li>\n * <li>关于事务必须执行到flushStatements才具有意义{@link org.apache.ibatis.executor.BatchExecutor#doFlushStatements(boolean)}</>\n * <li>返回值为批处理结果,如果对返回值比较关心的可接收判断处理</li>\n * <li>saveOrUpdate尽量少用把,保持批处理为简单的插入或更新</li>\n * <li>关于saveOrUpdate中的sqlSession,如果执行了select操作的话,BatchExecutor都会触发一次flushStatements,为了保证结果集,故使用包装了部分sqlSession查询操作</li>\n * <li>autoCommit参数,在spring下使用的是{@link org.mybatis.spring.transaction.SpringManagedTransaction},控制无效,只能通过datasource控制(建议不要修改),单独使用mybatis下{@link org.apache.ibatis.transaction.jdbc.JdbcTransaction}是可用的</li>\n * <pre>\n *     Spring示例:\n * \t\ttransactionTemplate.execute(new TransactionCallback<List<BatchResult>>() {\n *            {@code @Override}\n * \t\t\tpublic List<BatchResult> doInTransaction(TransactionStatus status) {\n * \t\t\t\tMybatisBatch.Method<Demo> method = new MybatisBatch.Method<>(DemoMapper.class);\n * \t\t\t\treturn new MybatisBatch<>(sqlSessionFactory,demoList).execute(true, method.insert());\n *            }\n *        });\n * </pre>\n *\n * @author nieqiurong\n * @since 3.5.4\n */\npublic class MybatisBatch<T> {\n\n    private final SqlSessionFactory sqlSessionFactory;\n\n    private final Collection<T> dataList;\n\n    private final int batchSize;\n\n    public MybatisBatch(SqlSessionFactory sqlSessionFactory, Collection<T> dataList) {\n        this.sqlSessionFactory = sqlSessionFactory;\n        this.dataList = dataList;\n        this.batchSize = Constants.DEFAULT_BATCH_SIZE;\n    }\n\n    public MybatisBatch(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, int batchSize) {\n        this.sqlSessionFactory = sqlSessionFactory;\n        this.dataList = dataList;\n        this.batchSize = batchSize;\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param statement 执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(String statement) {\n        return execute(false, statement, (entity) -> entity);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param statement        执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert 参数转换器\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(String statement, ParameterConvert<T> parameterConvert) {\n        return execute(false, statement, parameterConvert);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param autoCommit 是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement  执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(boolean autoCommit, String statement) {\n        return execute(autoCommit, statement, (entity) -> entity);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param batchMethod 批量操作方法\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(BatchMethod<T> batchMethod) {\n        return execute(false, batchMethod);\n    }\n\n\n    /**\n     * 执行批量操作\n     *\n     * @param autoCommit  是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param batchMethod 批量操作方法\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(boolean autoCommit, BatchMethod<T> batchMethod) {\n        return execute(autoCommit, batchMethod.getStatementId(), batchMethod.getParameterConvert());\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param autoCommit       是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement        执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert 参数转换器\n     * @return 批处理结果\n     */\n    public List<BatchResult> execute(boolean autoCommit, String statement, ParameterConvert<T> parameterConvert) {\n        List<BatchResult> resultList = new ArrayList<>(dataList.size());\n        try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, autoCommit)) {\n            List<List<T>> split = CollectionUtils.split(dataList, batchSize);\n            for (List<T> splitedList : split) {\n                for (T data : splitedList) {\n                    sqlSession.update(statement, toParameter(parameterConvert, data));\n                }\n                resultList.addAll(sqlSession.flushStatements());\n                if (!autoCommit) {\n                    sqlSession.commit();\n                }\n            }\n            return resultList;\n        }\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param insertMethod    插入方法\n     * @param insertPredicate 插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod    更新方法\n     * @return 批处理结果\n     */\n    public List<BatchResult> saveOrUpdate(BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod) {\n        return saveOrUpdate(false, insertMethod, insertPredicate, updateMethod);\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param autoCommit      是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param insertMethod    插入方法\n     * @param insertPredicate 插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod    更新方法\n     * @return 批处理结果\n     */\n    public List<BatchResult> saveOrUpdate(boolean autoCommit, BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod) {\n        List<BatchResult> resultList = new ArrayList<>();\n        try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, autoCommit)) {\n            BatchSqlSession session = new BatchSqlSession(sqlSession);\n            for (T data : dataList) {\n                if (insertPredicate.test(session, data)) {\n                    sqlSession.insert(insertMethod.getStatementId(), toParameter(insertMethod.getParameterConvert(), data));\n                } else {\n                    sqlSession.update(updateMethod.getStatementId(), toParameter(updateMethod.getParameterConvert(), data));\n                }\n            }\n            resultList.addAll(sqlSession.flushStatements());\n            resultList.addAll(session.getResultBatchList());\n            if (!autoCommit) {\n                sqlSession.commit();\n            }\n            return resultList;\n        }\n    }\n\n    /**\n     * 参数转换\n     *\n     * @param parameterConvert 参数转换器\n     * @param data             参数\n     * @return 方法参数\n     */\n    protected Object toParameter(ParameterConvert<T> parameterConvert, T data) {\n        return parameterConvert != null ? parameterConvert.convert(data) : data;\n    }\n\n    /**\n     * 内置方法简化调用\n     *\n     * @param <T> 泛型参数(实体)\n     */\n    public static class Method<T> {\n\n        /**\n         * 命名空间\n         */\n        private final String namespace;\n\n        public Method(Class<?> mapperClass) {\n            this.namespace = mapperClass.getName();\n        }\n\n        /**\n         * 新增方法\n         *\n         * @return 新增方法\n         */\n        public BatchMethod<T> insert() {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.INSERT_ONE.getMethod());\n        }\n\n        /**\n         * 新增方法\n         *\n         * @param function 转换函数\n         * @param <E>      实体\n         * @return 新增方法\n         */\n        public <E> BatchMethod<E> insert(Function<E, T> function) {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.INSERT_ONE.getMethod(), function::apply);\n        }\n\n        /**\n         * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#updateById(java.lang.Object)}\n         *\n         * @return 更新方法\n         */\n        public BatchMethod<T> updateById() {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE_BY_ID.getMethod(), (entity) -> {\n                Map<String, Object> param = new HashMap<>();\n                param.put(Constants.ENTITY, entity);\n                return param;\n            });\n        }\n\n        /**\n         * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#updateById(java.lang.Object)}\n         *\n         * @param etFunction 实体转换\n         * @param <E>        实体\n         * @return 更新方法\n         */\n        public <E> BatchMethod<E> updateById(Function<E, T> etFunction) {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE_BY_ID.getMethod(), (parameter) -> {\n                Map<String, Object> param = new HashMap<>();\n                param.put(Constants.ENTITY, etFunction.apply(parameter));\n                return param;\n            });\n        }\n\n        /**\n         * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#update(java.lang.Object, com.baomidou.mybatisplus.core.conditions.Wrapper)}\n         *\n         * @param wrapperFunction 更新条件(不能为null)\n         * @param <E>             实体\n         * @return 更新方法\n         */\n        public <E> BatchMethod<E> update(Function<E, Wrapper<T>> wrapperFunction) {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE.getMethod(), (parameter) -> {\n                Map<String, Object> param = new HashMap<>();\n                param.put(Constants.WRAPPER, wrapperFunction.apply(parameter));\n                return param;\n            });\n        }\n\n        /**\n         * 更新方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#update(java.lang.Object, com.baomidou.mybatisplus.core.conditions.Wrapper)}\n         *\n         * @param entityFunction  实体参数\n         * @param wrapperFunction wrapper参数\n         * @param <E>             实体\n         * @return 更新方法\n         */\n        public <E> BatchMethod<E> update(Function<E, T> entityFunction, Function<E, Wrapper<T>> wrapperFunction) {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.UPDATE.getMethod(), (parameter) -> {\n                Map<String, Object> param = new HashMap<>();\n                param.put(Constants.ENTITY, entityFunction.apply(parameter));\n                param.put(Constants.WRAPPER, wrapperFunction.apply(parameter));\n                return param;\n            });\n        }\n\n        /**\n         * 删除方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Object)} or {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Serializable)}\n         *\n         * @param function 参数转换\n         * @param <E>      实体\n         * @return 删除方法\n         */\n        public <E> BatchMethod<E> deleteById(Function<E, T> function) {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.DELETE_BY_ID.getMethod(), function::apply);\n        }\n\n        /**\n         * 删除方法 {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Object)} or {@link com.baomidou.mybatisplus.core.mapper.BaseMapper#deleteById(Serializable)}\n         *\n         * @param <T> 实体\n         * @return 删除方法\n         */\n        @SuppressWarnings(\"TypeParameterHidesVisibleType\")\n        public <T> BatchMethod<T> deleteById() {\n            return new BatchMethod<>(namespace + StringPool.DOT + SqlMethod.DELETE_BY_ID.getMethod());\n        }\n\n        public <E> BatchMethod<E> get(String method) {\n            return new BatchMethod<>(namespace + StringPool.DOT + method);\n        }\n\n        public <E> BatchMethod<E> get(String method, ParameterConvert<E> parameterConvert) {\n            return new BatchMethod<>(namespace + StringPool.DOT + method, parameterConvert);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/batch/ParameterConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.batch;\n\n/**\n * @author nieqiurong\n * @since 3.5.4\n */\n@FunctionalInterface\npublic interface ParameterConvert<T> {\n\n    /**\n     * 转换当前实体参数为mapper方法参数\n     *\n     * @param parameter 参数对象\n     * @return mapper方法参数.\n     */\n    Object convert(T parameter);\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractLambdaWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.LambdaUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;\nimport com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport org.apache.ibatis.reflection.property.PropertyNamer;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * Lambda 语法使用 Wrapper\n * <p>统一处理解析 lambda 获取 column</p>\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\npublic abstract class AbstractLambdaWrapper<T, Children extends AbstractLambdaWrapper<T, Children>>\n    extends AbstractWrapper<T, SFunction<T, ?>, Children> {\n\n    private Map<String, ColumnCache> columnMap = null;\n    private boolean initColumnMap = false;\n\n    @Override\n    @SafeVarargs\n    protected final String columnsToString(SFunction<T, ?>... columns) {\n        return columnsToString(true, columns);\n    }\n\n    @SafeVarargs\n    protected final String columnsToString(boolean onlyColumn, SFunction<T, ?>... columns) {\n        return columnsToString(onlyColumn, CollectionUtils.toList(columns));\n    }\n\n    protected final String columnsToString(boolean onlyColumn, List<SFunction<T, ?>> columns) {\n        return columns.stream().map(i -> columnToString(i, onlyColumn)).collect(joining(StringPool.COMMA));\n    }\n\n    @Override\n    protected String columnToString(SFunction<T, ?> column) {\n        return columnToString(column, true);\n    }\n\n    protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {\n        ColumnCache cache = getColumnCache(column);\n        return onlyColumn ? cache.getColumn() : cache.getColumnSelect();\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children groupBy(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return super.groupBy(condition, column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderBy(boolean condition, boolean isAsc, SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return orderBy(condition, isAsc, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children groupBy(SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return doGroupBy(true, column, CollectionUtils.toList(columns));\n    }\n\n\n    @Override\n    public Children groupBy(boolean condition, SFunction<T, ?> column, List<SFunction<T, ?>> columns) {\n        return doGroupBy(condition,column,columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByAsc(SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return super.orderByAsc(column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByAsc(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return super.orderByAsc(condition, column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByDesc(SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return super.orderByDesc(column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByDesc(boolean condition, SFunction<T, ?> column, SFunction<T, ?>... columns) {\n        return super.orderByDesc(condition, column, columns);\n    }\n\n\n    /**\n     * 获取 SerializedLambda 对应的列信息，从 lambda 表达式中推测实体类\n     * <p>\n     * 如果获取不到列信息，那么本次条件组装将会失败\n     *\n     * @return 列\n     * @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常\n     */\n    protected ColumnCache getColumnCache(SFunction<T, ?> column) {\n        LambdaMeta meta = LambdaUtils.extract(column);\n        String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());\n        Class<?> instantiatedClass = meta.getInstantiatedClass();\n        tryInitCache(instantiatedClass);\n        return getColumnCache(fieldName, instantiatedClass);\n    }\n\n    private void tryInitCache(Class<?> lambdaClass) {\n        if (!initColumnMap) {\n            final Class<T> entityClass = getEntityClass();\n            if (entityClass != null) {\n                lambdaClass = entityClass;\n            }\n            columnMap = LambdaUtils.getColumnMap(lambdaClass);\n            Assert.notNull(columnMap, \"can not find lambda cache for this entity [%s]\", lambdaClass.getName());\n            initColumnMap = true;\n        }\n    }\n\n    private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {\n        ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));\n        Assert.notNull(columnCache, \"can not find lambda cache for this property [%s] of entity [%s]\",\n            fieldName, lambdaClass.getName());\n        return columnCache;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.annotation.OrderBy;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Compare;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Func;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Join;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Nested;\nimport com.baomidou.mybatisplus.core.conditions.segments.ColumnSegment;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\nimport com.baomidou.mybatisplus.core.enums.SqlLike;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;\nimport lombok.Getter;\n\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.BiPredicate;\nimport java.util.function.Consumer;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.*;\nimport static com.baomidou.mybatisplus.core.enums.WrapperKeyword.APPLY;\nimport static java.util.stream.Collectors.joining;\n\n/**\n * 查询条件封装\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\n@SuppressWarnings({\"unchecked\"})\npublic abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>> extends Wrapper<T>\n    implements Compare<Children, R>, Nested<Children, Children>, Join<Children>, Func<Children, R> {\n\n    /**\n     * 占位符\n     */\n    protected final Children typedThis = (Children) this;\n    /**\n     * 必要度量\n     */\n    protected AtomicInteger paramNameSeq;\n    @Getter\n    protected Map<String, Object> paramNameValuePairs;\n    /**\n     * 其他\n     */\n    protected SharedString paramAlias;\n    protected SharedString lastSql;\n    /**\n     * SQL注释\n     */\n    protected SharedString sqlComment;\n    /**\n     * SQL起始语句\n     */\n    protected SharedString sqlFirst;\n    /**\n     * 数据库表映射实体类\n     */\n    private T entity;\n    protected MergeSegments expression;\n    /**\n     * 实体类型(主要用于确定泛型以及取TableInfo缓存)\n     */\n    private Class<T> entityClass;\n\n    @Override\n    public T getEntity() {\n        return entity;\n    }\n\n    public Children setEntity(T entity) {\n        this.entity = entity;\n        return typedThis;\n    }\n\n    public Class<T> getEntityClass() {\n        if (entityClass == null && entity != null) {\n            entityClass = (Class<T>) entity.getClass();\n        }\n        return entityClass;\n    }\n\n    public Children setEntityClass(Class<T> entityClass) {\n        if (entityClass != null) {\n            this.entityClass = entityClass;\n        }\n        return typedThis;\n    }\n\n    @Override\n    public <V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull) {\n        if (condition && CollectionUtils.isNotEmpty(params)) {\n            params.forEach((k, v) -> {\n                if (StringUtils.checkValNotNull(v)) {\n                    eq(k, v);\n                } else {\n                    if (null2IsNull) {\n                        isNull(k);\n                    }\n                }\n            });\n        }\n        return typedThis;\n    }\n\n    @Override\n    public <V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {\n        if (condition && CollectionUtils.isNotEmpty(params)) {\n            params.forEach((k, v) -> {\n                if (filter.test(k, v)) {\n                    if (StringUtils.checkValNotNull(v)) {\n                        eq(k, v);\n                    } else {\n                        if (null2IsNull) {\n                            isNull(k);\n                        }\n                    }\n                }\n            });\n        }\n        return typedThis;\n    }\n\n    @Override\n    public Children eq(boolean condition, R column, Object val) {\n        return addCondition(condition, column, EQ, val);\n    }\n\n    @Override\n    public Children ne(boolean condition, R column, Object val) {\n        return addCondition(condition, column, NE, val);\n    }\n\n    @Override\n    public Children gt(boolean condition, R column, Object val) {\n        return addCondition(condition, column, GT, val);\n    }\n\n    @Override\n    public Children ge(boolean condition, R column, Object val) {\n        return addCondition(condition, column, GE, val);\n    }\n\n    @Override\n    public Children lt(boolean condition, R column, Object val) {\n        return addCondition(condition, column, LT, val);\n    }\n\n    @Override\n    public Children le(boolean condition, R column, Object val) {\n        return addCondition(condition, column, LE, val);\n    }\n\n    @Override\n    public Children like(boolean condition, R column, Object val) {\n        return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT);\n    }\n\n    @Override\n    public Children notLike(boolean condition, R column, Object val) {\n        return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT);\n    }\n\n    @Override\n    public Children likeLeft(boolean condition, R column, Object val) {\n        return likeValue(condition, LIKE, column, val, SqlLike.LEFT);\n    }\n\n    @Override\n    public Children likeRight(boolean condition, R column, Object val) {\n        return likeValue(condition, LIKE, column, val, SqlLike.RIGHT);\n    }\n\n    @Override\n    public Children notLikeLeft(boolean condition, R column, Object val) {\n        return likeValue(condition, NOT_LIKE, column, val, SqlLike.LEFT);\n    }\n\n    @Override\n    public Children notLikeRight(boolean condition, R column, Object val) {\n        return likeValue(condition, NOT_LIKE, column, val, SqlLike.RIGHT);\n    }\n\n    @Override\n    public Children between(boolean condition, R column, Object val1, Object val2) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), BETWEEN,\n            () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));\n    }\n\n    @Override\n    public Children notBetween(boolean condition, R column, Object val1, Object val2) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_BETWEEN,\n            () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));\n    }\n\n    @Override\n    public Children and(boolean condition, Consumer<Children> consumer) {\n        return and(condition).addNestedCondition(condition, consumer);\n    }\n\n    @Override\n    public Children or(boolean condition, Consumer<Children> consumer) {\n        return or(condition).addNestedCondition(condition, consumer);\n    }\n\n    @Override\n    public Children nested(boolean condition, Consumer<Children> consumer) {\n        return addNestedCondition(condition, consumer);\n    }\n\n    @Override\n    public Children not(boolean condition, Consumer<Children> consumer) {\n        return not(condition).addNestedCondition(condition, consumer);\n    }\n\n    @Override\n    public Children or(boolean condition) {\n        return maybeDo(condition, () -> appendSqlSegments(OR));\n    }\n\n    @Override\n    public Children apply(boolean condition, String applySql, Object... values) {\n        return maybeDo(condition, () -> appendSqlSegments(APPLY, () -> formatSqlMaybeWithParam(applySql, values)));\n    }\n\n    @Override\n    public Children last(boolean condition, String lastSql) {\n        if (condition) {\n            this.lastSql.setStringValue(StringPool.SPACE + lastSql);\n        }\n        return typedThis;\n    }\n\n    @Override\n    public Children comment(boolean condition, String comment) {\n        if (condition) {\n            this.sqlComment.setStringValue(comment);\n        }\n        return typedThis;\n    }\n\n    @Override\n    public Children first(boolean condition, String firstSql) {\n        if (condition) {\n            this.sqlFirst.setStringValue(firstSql);\n        }\n        return typedThis;\n    }\n\n    @Override\n    public Children exists(boolean condition, String existsSql, Object... values) {\n        return maybeDo(condition, () -> appendSqlSegments(EXISTS,\n            () -> String.format(\"(%s)\", formatSqlMaybeWithParam(existsSql, values))));\n    }\n\n    @Override\n    public Children notExists(boolean condition, String existsSql, Object... values) {\n        return not(condition).exists(condition, existsSql, values);\n    }\n\n    @Override\n    public Children isNull(boolean condition, R column) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NULL));\n    }\n\n    @Override\n    public Children isNotNull(boolean condition, R column) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IS_NOT_NULL));\n    }\n\n    @Override\n    public Children in(boolean condition, R column, Collection<?> coll) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(coll)));\n    }\n\n    @Override\n    public Children in(boolean condition, R column, Object... values) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN, inExpression(values)));\n    }\n\n    @Override\n    public Children notIn(boolean condition, R column, Collection<?> coll) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(coll)));\n    }\n\n    @Override\n    public Children notIn(boolean condition, R column, Object... values) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN, inExpression(values)));\n    }\n\n    @Override\n    public Children eqSql(boolean condition, R column, String eqValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), EQ,\n            () -> String.format(\"(%s)\", eqValue)));\n    }\n\n    @Override\n    public Children inSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), IN,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children gtSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GT,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children geSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), GE,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children ltSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LT,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children leSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), LE,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children notInSql(boolean condition, R column, String inValue) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), NOT_IN,\n            () -> String.format(\"(%s)\", inValue)));\n    }\n\n    @Override\n    public Children groupBy(boolean condition, R column, R... columns) {\n        return doGroupBy(condition, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    public Children groupBy(boolean condition, R column, List<R> columns) {\n        return doGroupBy(condition, column, columns);\n    }\n\n    public Children doGroupBy(boolean condition, R column, List<R> columns) {\n        return maybeDo(condition, () -> {\n            String one = StringPool.EMPTY;\n            if (column != null) {\n                one = columnToString(column);\n            }\n            if (CollectionUtils.isNotEmpty(columns)) {\n                one += column != null ? StringPool.COMMA + columnsToString(columns) : columnsToString(columns);\n            }\n            final String finalOne = one;\n            appendSqlSegments(GROUP_BY, () -> finalOne);\n        });\n    }\n\n\n    public Children doOrderBy(boolean condition, boolean isAsc, R column, List<R> columns) {\n        return maybeDo(condition, () -> {\n            final SqlKeyword mode = isAsc ? ASC : DESC;\n            if (column != null) {\n                appendSqlSegments(ORDER_BY, columnToSqlSegment(column), mode);\n            }\n            if (CollectionUtils.isNotEmpty(columns)) {\n                columns.forEach(c -> appendSqlSegments(ORDER_BY,\n                    columnToSqlSegment(c), mode));\n            }\n        });\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, R column, R... columns) {\n        return doOrderBy(condition, isAsc, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, R column, List<R> columns) {\n        return doOrderBy(condition, isAsc, column, columns);\n    }\n\n    @Override\n    public Children groupBy(boolean condition, R column) {\n        return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column)));\n    }\n\n    @Override\n    public Children groupBy(boolean condition, List<R> columns) {\n        return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnsToString(columns)));\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, R column) {\n        return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(column),\n            isAsc ? ASC : DESC));\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, List<R> columns) {\n        return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY,\n            columnToSqlSegment(c), isAsc ? ASC : DESC)));\n    }\n\n    @Override\n    public Children having(boolean condition, String sqlHaving, Object... params) {\n        return maybeDo(condition, () -> appendSqlSegments(HAVING, () -> formatSqlMaybeWithParam(sqlHaving, params)));\n    }\n\n    @Override\n    public Children func(boolean condition, Consumer<Children> consumer) {\n        return maybeDo(condition, () -> consumer.accept(typedThis));\n    }\n\n    /**\n     * 内部自用\n     * <p>NOT 关键词</p>\n     */\n    protected Children not(boolean condition) {\n        return maybeDo(condition, () -> appendSqlSegments(NOT));\n    }\n\n    /**\n     * 内部自用\n     * <p>拼接 AND</p>\n     */\n    protected Children and(boolean condition) {\n        return maybeDo(condition, () -> appendSqlSegments(AND));\n    }\n\n    /**\n     * 内部自用\n     * <p>拼接 LIKE 以及 值</p>\n     */\n    protected Children likeValue(boolean condition, SqlKeyword keyword, R column, Object val, SqlLike sqlLike) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword,\n            () -> formatParam(null, SqlUtils.concatLike(val, sqlLike))));\n    }\n\n    /**\n     * 普通查询条件\n     *\n     * @param condition  是否执行\n     * @param column     属性\n     * @param sqlKeyword SQL 关键词\n     * @param val        条件值\n     */\n    protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {\n        return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword,\n            () -> formatParam(null, val)));\n    }\n\n    /**\n     * 多重嵌套查询条件\n     *\n     * @param condition 查询条件值\n     */\n    protected Children addNestedCondition(boolean condition, Consumer<Children> consumer) {\n        return maybeDo(condition, () -> {\n            final Children instance = instance();\n            consumer.accept(instance);\n            appendSqlSegments(APPLY, instance);\n        });\n    }\n\n    /**\n     * 子类返回一个自己的新对象\n     */\n    protected abstract Children instance();\n\n    /**\n     * 格式化 sql\n     * <p>\n     * 支持 \"{0}\" 这种,或者 \"sql {0} sql\" 这种\n     * 也支持 \"sql {0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler} sql\" 这种\n     *\n     * @param sqlStr 可能是sql片段\n     * @param params 参数\n     * @return sql片段\n     */\n    @SuppressWarnings(\"SameParameterValue\")\n    protected final String formatSqlMaybeWithParam(String sqlStr, Object... params) {\n        if (StringUtils.isBlank(sqlStr)) {\n            return null;\n        }\n        if (ArrayUtils.isNotEmpty(params)) {\n            for (int i = 0; i < params.length; ++i) {\n                String target = Constants.LEFT_BRACE + i + Constants.RIGHT_BRACE;\n                if (sqlStr.contains(target)) {\n                    sqlStr = sqlStr.replace(target, formatParam(null, params[i]));\n                } else {\n                    Matcher matcher = Pattern.compile(\"[{]\" + i + \",[a-zA-Z0-9.,=]+}\").matcher(sqlStr);\n                    if (!matcher.find()) {\n                        throw ExceptionUtils.mpe(\"Please check the syntax correctness! sql not contains: \\\"%s\\\"\", target);\n                    }\n                    String group = matcher.group();\n                    sqlStr = sqlStr.replace(group, formatParam(group.substring(target.length(), group.length() - 1), params[i]));\n                }\n            }\n        }\n        return sqlStr;\n    }\n\n    /**\n     * 处理入参\n     *\n     * @param mapping 例如: \"javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler\" 这种\n     * @param param   参数\n     * @return value\n     */\n    protected final String formatParam(String mapping, Object param) {\n        final String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();\n        final String paramStr = getParamAlias() + Constants.WRAPPER_PARAM_MIDDLE + genParamName;\n        paramNameValuePairs.put(genParamName, param);\n        return SqlScriptUtils.safeParam(paramStr, mapping);\n    }\n\n    /**\n     * 函数化的做事\n     *\n     * @param condition 做不做\n     * @param something 做什么\n     * @return Children\n     */\n    protected final Children maybeDo(boolean condition, DoSomething something) {\n        if (condition) {\n            something.doIt();\n        }\n        return typedThis;\n    }\n\n    /**\n     * 获取in表达式 包含括号\n     *\n     * @param value 集合\n     */\n    protected ISqlSegment inExpression(Collection<?> value) {\n        if (CollectionUtils.isEmpty(value)) {\n            return () -> \"()\";\n        }\n        return () -> value.stream().map(i -> formatParam(null, i))\n            .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));\n    }\n\n    /**\n     * 获取in表达式 包含括号\n     *\n     * @param values 数组\n     */\n    protected ISqlSegment inExpression(Object[] values) {\n        if (ArrayUtils.isEmpty(values)) {\n            return () -> \"()\";\n        }\n        return () -> Arrays.stream(values).map(i -> formatParam(null, i))\n            .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));\n    }\n\n    /**\n     * 必要的初始化\n     */\n    protected void initNeed() {\n        paramNameSeq = new AtomicInteger(0);\n        paramNameValuePairs = new HashMap<>(16);\n        expression = new MergeSegments();\n        lastSql = SharedString.emptyString();\n        sqlComment = SharedString.emptyString();\n        sqlFirst = SharedString.emptyString();\n    }\n\n    @Override\n    public void clear() {\n        entity = null;\n        paramNameSeq.set(0);\n        paramNameValuePairs.clear();\n        expression.clear();\n        lastSql.toEmpty();\n        sqlComment.toEmpty();\n        sqlFirst.toEmpty();\n    }\n\n    /**\n     * 添加 where 片段\n     *\n     * @param sqlSegments ISqlSegment 数组\n     */\n    protected void appendSqlSegments(ISqlSegment... sqlSegments) {\n        expression.add(sqlSegments);\n    }\n\n    /**\n     * 是否使用默认注解 {@link OrderBy} 排序\n     *\n     * @return true 使用 false 不使用\n     */\n    public boolean isUseAnnotationOrderBy() {\n        final String _sqlSegment = this.getSqlSegment();\n        if (StringUtils.isBlank(_sqlSegment)) {\n            return true;\n        }\n        final String _sqlSegmentUpper = _sqlSegment.toUpperCase();\n        return !(_sqlSegmentUpper.contains(Constants.ORDER_BY) || _sqlSegmentUpper.contains(Constants.LIMIT));\n    }\n\n    @Override\n    public String getSqlSegment() {\n        return expression.getSqlSegment() + lastSql.getStringValue();\n    }\n\n    @Override\n    public String getSqlComment() {\n        if (StringUtils.isNotBlank(sqlComment.getStringValue())) {\n            return \"/*\" + sqlComment.getStringValue() + \"*/\";\n        }\n        return null;\n    }\n\n    @Override\n    public String getSqlFirst() {\n        if (StringUtils.isNotBlank(sqlFirst.getStringValue())) {\n            return sqlFirst.getStringValue();\n        }\n        return null;\n    }\n\n    @Override\n    public MergeSegments getExpression() {\n        return expression;\n    }\n\n    public String getParamAlias() {\n        return paramAlias == null ? Constants.WRAPPER : paramAlias.getStringValue();\n    }\n\n    /**\n     * 参数别名设置，初始化时优先设置该值、重复设置异常\n     *\n     * @param paramAlias 参数别名\n     * @return Children\n     */\n    @SuppressWarnings(\"unused\")\n    public Children setParamAlias(String paramAlias) {\n        Assert.notEmpty(paramAlias, \"paramAlias can not be empty!\");\n        Assert.isEmpty(paramNameValuePairs, \"Please call this method before working!\");\n        Assert.isNull(this.paramAlias, \"Please do not call the method repeatedly!\");\n        this.paramAlias = new SharedString(paramAlias);\n        return typedThis;\n    }\n\n    /**\n     * 获取 columnName\n     */\n    protected final ColumnSegment columnToSqlSegment(R column) {\n        return () -> columnToString(column);\n    }\n\n    /**\n     * 获取 columnName\n     */\n    protected String columnToString(R column) {\n        return (String) column;\n    }\n\n    /**\n     * 获取 columnNames\n     */\n    protected String columnsToString(R... columns) {\n        return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA));\n    }\n\n    /**\n     * 多字段转换为逗号 \",\" 分割字符串\n     *\n     * @param columns 多字段\n     */\n    protected String columnsToString(List<R> columns) {\n        return columns.stream().map(this::columnToString).collect(joining(StringPool.COMMA));\n    }\n\n    @Override\n    @SuppressWarnings(\"all\")\n    public Children clone() {\n        return SerializationUtils.clone(typedThis);\n    }\n\n    /**\n     * 做事函数\n     */\n    @FunctionalInterface\n    public interface DoSomething {\n\n        void doIt();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/ISqlSegment.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions;\n\n\nimport java.io.Serializable;\n\n/**\n * SQL 片段接口\n *\n * @author hubin miemie HCL\n * @since 2018-05-28\n */\n@FunctionalInterface\npublic interface ISqlSegment extends Serializable {\n\n    /**\n     * SQL 片段\n     */\n    String getSqlSegment();\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/SharedString.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.experimental.Accessors;\n\nimport java.io.Serializable;\n\n/**\n * 共享查询字段\n *\n * @author miemie\n * @since 2018-11-20\n */\n@Data\n@Accessors(chain = true)\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SharedString implements Serializable {\n    private static final long serialVersionUID = -1536422416594422874L;\n\n    /**\n     * 共享的 string 值\n     */\n    private String stringValue;\n\n    /**\n     * SharedString 里是 \"\"\n     */\n    public static SharedString emptyString() {\n        return new SharedString(StringPool.EMPTY);\n    }\n\n    /**\n     * 置 empty\n     *\n     * @since 3.3.1\n     */\n    public void toEmpty() {\n        stringValue = StringPool.EMPTY;\n    }\n\n    /**\n     * 置 null\n     *\n     * @since 3.3.1\n     */\n    public void toNull() {\n        stringValue = null;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/Wrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.util.Objects;\n\n/**\n * 条件构造抽象类\n *\n * @author hubin\n * @since 2018-05-25\n */\n@SuppressWarnings(\"all\")\npublic abstract class Wrapper<T> implements ISqlSegment {\n\n    /**\n     * 实体对象（子类实现）\n     *\n     * @return 泛型 T\n     */\n    public abstract T getEntity();\n\n    public String getSqlSelect() {\n        return null;\n    }\n\n    public String getSqlSet() {\n        return null;\n    }\n\n    public String getSqlComment() {\n        return null;\n    }\n\n    public String getSqlFirst() {\n        return null;\n    }\n\n    /**\n     * 获取 MergeSegments\n     */\n    public abstract MergeSegments getExpression();\n\n    /**\n     * 获取自定义SQL 简化自定义XML复杂情况\n     * <p>\n     * 使用方法: `select xxx from table` + ${ew.customSqlSegment}\n     * <p>\n     * 注意事项:\n     * 1. 逻辑删除需要自己拼接条件 (之前自定义也同样)\n     * 2. 不支持wrapper中附带实体的情况 (wrapper自带实体会更麻烦)\n     * 3. 用法 ${ew.customSqlSegment} (不需要where标签包裹,切记!)\n     * 4. ew是wrapper定义别名,不能使用其他的替换\n     */\n    public String getCustomSqlSegment() {\n        MergeSegments expression = getExpression();\n        if (Objects.nonNull(expression)) {\n            NormalSegmentList normal = expression.getNormal();\n            String sqlSegment = getSqlSegment();\n            if (StringUtils.isNotBlank(sqlSegment)) {\n                if (normal.isEmpty()) {\n                    return sqlSegment;\n                } else {\n                    return Constants.WHERE + StringPool.SPACE + sqlSegment;\n                }\n            }\n        }\n        return StringPool.EMPTY;\n    }\n\n    /**\n     * 查询条件为空(包含entity)\n     */\n    public boolean isEmptyOfWhere() {\n        return isEmptyOfNormal() && isEmptyOfEntity();\n    }\n\n    /**\n     * 查询条件不为空(包含entity)\n     */\n    public boolean isNonEmptyOfWhere() {\n        return !isEmptyOfWhere();\n    }\n\n    @Deprecated\n    public boolean nonEmptyOfWhere() {\n        return isNonEmptyOfWhere();\n    }\n\n    /**\n     * 查询条件为空(不包含entity)\n     */\n    public boolean isEmptyOfNormal() {\n        return CollectionUtils.isEmpty(getExpression().getNormal());\n    }\n\n    /**\n     * 查询条件为空(不包含entity)\n     */\n    public boolean isNonEmptyOfNormal() {\n        return !isEmptyOfNormal();\n    }\n\n    @Deprecated\n    public boolean nonEmptyOfNormal() {\n        return isNonEmptyOfNormal();\n    }\n\n    /**\n     * 深层实体判断属性\n     *\n     * @return true 不为空\n     */\n    public boolean isNonEmptyOfEntity() {\n        T entity = getEntity();\n        if (entity == null) {\n            return false;\n        }\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());\n        if (tableInfo == null) {\n            return false;\n        }\n        if (tableInfo.getFieldList().stream().anyMatch(e -> fieldStrategyMatch(tableInfo, entity, e))) {\n            return true;\n        }\n        return StringUtils.isNotBlank(tableInfo.getKeyProperty()) ? Objects.nonNull(tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty())) : false;\n    }\n\n    @Deprecated\n    public boolean nonEmptyOfEntity() {\n        return isNonEmptyOfEntity();\n    }\n\n    /**\n     * 根据实体FieldStrategy属性来决定判断逻辑\n     */\n    private boolean fieldStrategyMatch(TableInfo tableInfo, T entity, TableFieldInfo e) {\n        switch (e.getWhereStrategy()) {\n            case NOT_NULL:\n                return Objects.nonNull(tableInfo.getPropertyValue(entity, e.getProperty()));\n            case ALWAYS:\n                return true;\n            case NOT_EMPTY:\n                return StringUtils.checkValNotNull(tableInfo.getPropertyValue(entity, e.getProperty()));\n            case NEVER:\n                return false;\n            default:\n                return Objects.nonNull(tableInfo.getPropertyValue(entity, e.getProperty()));\n        }\n    }\n\n    /**\n     * 深层实体判断属性\n     *\n     * @return true 为空\n     */\n    public boolean isEmptyOfEntity() {\n        return !isNonEmptyOfEntity();\n    }\n\n    /**\n     * 获取格式化后的执行sql\n     *\n     * @return sql\n     * @since 3.3.1\n     */\n    public String getTargetSql() {\n        return getSqlSegment().replaceAll(\"#\\\\{.+?}\", \"?\");\n    }\n\n    /**\n     * 条件清空\n     *\n     * @since 3.3.1\n     */\n    abstract public void clear();\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Compare.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.interfaces;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.function.BiPredicate;\n\n/**\n * 查询条件封装\n * <p>比较值</p>\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\npublic interface Compare<Children, R> extends Serializable {\n\n    /**\n     * map 所有非空属性等于 =\n     *\n     * @param params map 类型的参数, key 是字段名, value 是字段值\n     * @return children\n     */\n    default <V> Children allEq(Map<R, V> params) {\n        return allEq(params, true);\n    }\n\n    /**\n     * map 所有非空属性等于 =\n     *\n     * @param params      map 类型的参数, key 是字段名, value 是字段值\n     * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段\n     * @return children\n     */\n    default <V> Children allEq(Map<R, V> params, boolean null2IsNull) {\n        return allEq(true, params, null2IsNull);\n    }\n\n    /**\n     * map 所有非空属性等于 =\n     *\n     * @param condition   执行条件\n     * @param params      map 类型的参数, key 是字段名, value 是字段值\n     * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段\n     * @return children\n     */\n    <V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull);\n\n    /**\n     * 字段过滤接口，传入多参数时允许对参数进行过滤\n     *\n     * @param filter 返回 true 来允许字段传入比对条件中\n     * @param params map 类型的参数, key 是字段名, value 是字段值\n     * @return children\n     */\n    default <V> Children allEq(BiPredicate<R, V> filter, Map<R, V> params) {\n        return allEq(filter, params, true);\n    }\n\n    /**\n     * 字段过滤接口，传入多参数时允许对参数进行过滤\n     *\n     * @param filter      返回 true 来允许字段传入比对条件中\n     * @param params      map 类型的参数, key 是字段名, value 是字段值\n     * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段\n     * @return children\n     */\n    default <V> Children allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {\n        return allEq(true, filter, params, null2IsNull);\n    }\n\n    /**\n     * 字段过滤接口，传入多参数时允许对参数进行过滤\n     *\n     * @param condition   执行条件\n     * @param filter      返回 true 来允许字段传入比对条件中\n     * @param params      map 类型的参数, key 是字段名, value 是字段值\n     * @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段\n     * @return children\n     */\n    <V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull);\n\n    /**\n     * 等于 =\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children eq(R column, Object val) {\n        return eq(true, column, val);\n    }\n\n    /**\n     * 等于 =\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children eq(boolean condition, R column, Object val);\n\n    /**\n     * 不等于 &lt;&gt;\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children ne(R column, Object val) {\n        return ne(true, column, val);\n    }\n\n    /**\n     * 不等于 &lt;&gt;\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children ne(boolean condition, R column, Object val);\n\n    /**\n     * 大于 &gt;\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children gt(R column, Object val) {\n        return gt(true, column, val);\n    }\n\n    /**\n     * 大于 &gt;\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children gt(boolean condition, R column, Object val);\n\n    /**\n     * 大于等于 &gt;=\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children ge(R column, Object val) {\n        return ge(true, column, val);\n    }\n\n    /**\n     * 大于等于 &gt;=\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children ge(boolean condition, R column, Object val);\n\n    /**\n     * 小于 &lt;\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children lt(R column, Object val) {\n        return lt(true, column, val);\n    }\n\n    /**\n     * 小于 &lt;\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children lt(boolean condition, R column, Object val);\n\n    /**\n     * 小于等于 &lt;=\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children le(R column, Object val) {\n        return le(true, column, val);\n    }\n\n    /**\n     * 小于等于 &lt;=\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children le(boolean condition, R column, Object val);\n\n\n    /**\n     * BETWEEN 值1 AND 值2\n     *\n     * @param column    字段\n     * @param val1      值1\n     * @param val2      值2\n     * @return children\n     */\n    default Children between(R column, Object val1, Object val2) {\n        return between(true, column, val1, val2);\n    }\n\n    /**\n     * BETWEEN 值1 AND 值2\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val1      值1\n     * @param val2      值2\n     * @return children\n     */\n    Children between(boolean condition, R column, Object val1, Object val2);\n\n    /**\n     * NOT BETWEEN 值1 AND 值2\n     *\n     * @param column    字段\n     * @param val1      值1\n     * @param val2      值2\n     * @return children\n     */\n    default Children notBetween(R column, Object val1, Object val2) {\n        return notBetween(true, column, val1, val2);\n    }\n\n    /**\n     * NOT BETWEEN 值1 AND 值2\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val1      值1\n     * @param val2      值2\n     * @return children\n     */\n    Children notBetween(boolean condition, R column, Object val1, Object val2);\n\n    /**\n     * LIKE '%值%'\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children like(R column, Object val) {\n        return like(true, column, val);\n    }\n\n    /**\n     * LIKE '%值%'\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children like(boolean condition, R column, Object val);\n\n    /**\n     * NOT LIKE '%值%'\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children notLike(R column, Object val) {\n        return notLike(true, column, val);\n    }\n\n    /**\n     * NOT LIKE '%值%'\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children notLike(boolean condition, R column, Object val);\n\n    /**\n     * NOT LIKE '%值'\n     *\n     * @param column 字段\n     * @param val    值\n     * @return children\n     */\n    default Children notLikeLeft(R column, Object val) {\n        return notLikeLeft(true, column, val);\n    }\n\n    /**\n     * NOT LIKE '%值'\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children notLikeLeft(boolean condition, R column, Object val);\n\n    /**\n     * NOT LIKE '值%'\n     *\n     * @param column 字段\n     * @param val 值\n     * @return children\n     */\n    default Children notLikeRight(R column, Object val) {\n        return notLikeRight(true, column, val);\n    }\n\n    /**\n     * NOT LIKE '值%'\n     *\n     * @param condition 执行条件\n     * @param column 字段\n     * @param val 值\n     * @return children\n     */\n    Children notLikeRight(boolean condition, R column, Object val);\n\n    /**\n     * LIKE '%值'\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children likeLeft(R column, Object val) {\n        return likeLeft(true, column, val);\n    }\n\n    /**\n     * LIKE '%值'\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children likeLeft(boolean condition, R column, Object val);\n\n    /**\n     * LIKE '值%'\n     *\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children likeRight(R column, Object val) {\n        return likeRight(true, column, val);\n    }\n\n    /**\n     * LIKE '值%'\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    Children likeRight(boolean condition, R column, Object val);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Func.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.interfaces;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * 查询条件封装\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\n@SuppressWarnings(\"unchecked\")\npublic interface Func<Children, R> extends Serializable {\n\n    /**\n     * 字段 IS NULL\n     * <p>例: isNull(\"name\")</p>\n     *\n     * @param column    字段\n     * @return children\n     */\n    default Children isNull(R column) {\n        return isNull(true, column);\n    }\n\n    /**\n     * 字段 IS NULL\n     * <p>例: isNull(true, \"name\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @return children\n     */\n    Children isNull(boolean condition, R column);\n\n    /**\n     * 字段 IS NOT NULL\n     * <p>例: isNotNull(\"name\")</p>\n     *\n     * @param column    字段\n     * @return children\n     */\n    default Children isNotNull(R column) {\n        return isNotNull(true, column);\n    }\n\n    /**\n     * 字段 IS NOT NULL\n     * <p>例: isNotNull(true, \"name\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @return children\n     */\n    Children isNotNull(boolean condition, R column);\n\n    /**\n     * 字段 IN (value.get(0), value.get(1), ...)\n     * <p>例: in(\"id\", Arrays.asList(1, 2, 3, 4, 5))</p>\n     *\n     * <li> 注意！当集合为 空或null 时, sql会拼接为：WHERE (字段名 IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param column    字段\n     * @param coll      数据集合\n     * @return children\n     */\n    default Children in(R column, Collection<?> coll) {\n        return in(true, column, coll);\n    }\n\n    /**\n     * 字段 IN (value.get(0), value.get(1), ...)\n     * <p>例: in(true, \"id\", Arrays.asList(1, 2, 3, 4, 5))</p>\n     *\n     * <li> 注意！当集合为 空或null 时, sql会拼接为：WHERE (字段名 IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param coll      数据集合\n     * @return children\n     */\n    Children in(boolean condition, R column, Collection<?> coll);\n\n    /**\n     * 字段 IN (v0, v1, ...)\n     * <p>例: in(\"id\", 1, 2, 3, 4, 5)</p>\n     *\n     * <li> 注意！当数组为 空或null 时, sql会拼接为：WHERE (字段名 IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param column    字段\n     * @param values    数据数组\n     * @return children\n     */\n    default Children in(R column, Object... values) {\n        return in(true, column, values);\n    }\n\n    /**\n     * 字段 IN (v0, v1, ...)\n     * <p>例: in(true, \"id\", 1, 2, 3, 4, 5)</p>\n     *\n     * <li> 注意！当数组为 空或null 时, sql会拼接为：WHERE (字段名 IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param values    数据数组\n     * @return children\n     */\n    Children in(boolean condition, R column, Object... values);\n\n    /**\n     * 字段 NOT IN (value.get(0), value.get(1), ...)\n     * <p>例: notIn(\"id\", Arrays.asList(1, 2, 3, 4, 5))</p>\n     *\n     * <li> 注意！当集合为 空或null 时, sql会拼接为：WHERE (字段名 NOT IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param column    字段\n     * @param coll      数据集合\n     * @return children\n     */\n    default Children notIn(R column, Collection<?> coll) {\n        return notIn(true, column, coll);\n    }\n\n    /**\n     * 字段 NOT IN (value.get(0), value.get(1), ...)\n     * <p>例: notIn(true, \"id\", Arrays.asList(1, 2, 3, 4, 5))</p>\n     *\n     * <li> 注意！当集合为 空或null 时, sql会拼接为：WHERE (字段名 NOT IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param coll      数据集合\n     * @return children\n     */\n    Children notIn(boolean condition, R column, Collection<?> coll);\n\n    /**\n     * 字段 NOT IN (v0, v1, ...)\n     * <p>例: notIn(\"id\", 1, 2, 3, 4, 5)</p>\n     *\n     * <li> 注意！当数组为 空或null 时, sql会拼接为：WHERE (字段名 NOT IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param column    字段\n     * @param values    数据数组\n     * @return children\n     */\n    default Children notIn(R column, Object... values) {\n        return notIn(true, column, values);\n    }\n\n    /**\n     * 字段 NOT IN (v0, v1, ...)\n     * <p>例: notIn(true, \"id\", 1, 2, 3, 4, 5)</p>\n     *\n     * <li> 注意！当数组为 空或null 时, sql会拼接为：WHERE (字段名 NOT IN ()), 执行时报错</li>\n     * <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param values    数据数组\n     * @return children\n     */\n    Children notIn(boolean condition, R column, Object... values);\n\n    /**\n     * 字段 EQ ( sql语句 )\n     * <p>!! sql 注入方式的 eq 方法 !!</p>\n     * <p>例1: eqSql(\"id\", \"1\")</p>\n     * <p>例2: eqSql(\"id\", \"select MAX(id) from table\")</p>\n     *\n     * @param column 字段\n     * @param sql    sql语句\n     * @return children\n     * @since 3.5.6\n     */\n    default Children eqSql(R column, String sql) {\n        return eqSql(true, column, sql);\n    }\n\n    /**\n     * 字段 EQ ( sql语句 )\n     * <p>!! sql 注入方式的 eq 方法 !!</p>\n     * <p>例1: eqSql(\"id\", \"1\")</p>\n     * <p>例2: eqSql(\"id\", \"select MAX(id) from table\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param sql       sql语句\n     * @return children\n     * @since 3.5.6\n     */\n    Children eqSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 IN ( sql语句 )\n     * <p>!! sql 注入方式的 in 方法 !!</p>\n     * <p>例1: inSql(\"id\", \"1\")</p>\n     * <p>例2: inSql(\"id\", \"select id from table where id &lt; 3\")</p>\n     *\n     * @param column    字段\n     * @param sql   sql语句\n     * @return children\n     */\n    default Children inSql(R column, String sql) {\n        return inSql(true, column, sql);\n    }\n\n    /**\n     * 字段 IN ( sql语句 )\n     * <p>!! sql 注入方式的 in 方法 !!</p>\n     * <p>例1: inSql(true, \"id\", \"1\")</p>\n     * <p>例2: inSql(true, \"id\", \"select id from table where id &lt; 3\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param sql   sql语句\n     * @return children\n     */\n    Children inSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 &gt; ( sql语句 )\n     * <p>例1: gtSql(true, \"id\", \"1\")</p>\n     * <p>例1: gtSql(true, \"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param sql       sql语句\n     * @return children\n     */\n    Children gtSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 &gt; ( sql语句 )\n     * <p>例1: gtSql(\"id\", \"1\")</p>\n     * <p>例1: gtSql(\"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param column 字段\n     * @param sql    sql语句\n     * @return children\n     */\n    default Children gtSql(R column, String sql) {\n        return gtSql(true, column, sql);\n    }\n\n    /**\n     * 字段 >= ( sql语句 )\n     * <p>例1: geSql(true, \"id\", \"1\")</p>\n     * <p>例1: geSql(true, \"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param sql       sql语句\n     * @return children\n     */\n    Children geSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 >= ( sql语句 )\n     * <p>例1: geSql(\"id\", \"1\")</p>\n     * <p>例1: geSql(\"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param column 字段\n     * @param sql    sql语句\n     * @return children\n     */\n    default Children geSql(R column, String sql) {\n        return geSql(true, column, sql);\n    }\n\n    /**\n     * 字段 &lt; ( sql语句 )\n     * <p>例1: ltSql(true, \"id\", \"1\")</p>\n     * <p>例1: ltSql(true , \"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param condition 执行条件\n     * @param column 字段\n     * @param sql sql语句\n     * @return children\n     */\n    Children ltSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 &lt; ( sql语句 )\n     * <p>例1: ltSql(\"id\", \"1\")</p>\n     * <p>例1: ltSql(\"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param column 字段\n     * @param sql    sql语句\n     * @return children\n     */\n    default Children ltSql(R column, String sql) {\n        return ltSql(true, column, sql);\n    }\n\n    /**\n     * 字段 <= ( sql语句 )\n     * <p>例1: leSql(true, \"id\", \"1\")</p>\n     * <p>例1: leSql(true ,\"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param sql       sql语句\n     * @return children\n     */\n    Children leSql(boolean condition, R column, String sql);\n\n    /**\n     * 字段 <= ( sql语句 )\n     * <p>例1: leSql(\"id\", \"1\")</p>\n     * <p>例1: leSql(\"id\", \"select id from table where name = 'JunJun'\")</p>\n     *\n     * @param column 字段\n     * @param inValue  sql语句\n     * @return children\n     */\n    default Children leSql(R column, String inValue) {\n        return leSql(true, column, inValue);\n    }\n\n    /**\n     * 字段 NOT IN ( sql语句 )\n     * <p>!! sql 注入方式的 not in 方法 !!</p>\n     * <p>例1: notInSql(\"id\", \"1, 2, 3, 4, 5, 6\")</p>\n     * <p>例2: notInSql(\"id\", \"select id from table where id &lt; 3\")</p>\n     *\n     * @param column  字段\n     * @param inValue sql语句 ---&gt; 1,2,3,4,5,6 或者 select id from table where id &lt; 3\n     * @return children\n     */\n    default Children notInSql(R column, String inValue) {\n        return notInSql(true, column, inValue);\n    }\n\n    /**\n     * 字段 NOT IN ( sql语句 )\n     * <p>!! sql 注入方式的 not in 方法 !!</p>\n     * <p>例1: notInSql(true, \"id\", \"1, 2, 3, 4, 5, 6\")</p>\n     * <p>例2: notInSql(true, \"id\", \"select id from table where id &lt; 3\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param inValue   sql语句 ---&gt; 1,2,3,4,5,6 或者 select id from table where id &lt; 3\n     * @return children\n     */\n    Children notInSql(boolean condition, R column, String inValue);\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(true, \"id\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @return children\n     */\n    Children groupBy(boolean condition, R column);\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(\"id\")</p>\n     *\n     * @param column 单个字段\n     * @return children\n     */\n    default Children groupBy(R column) {\n        return groupBy(true, column);\n    }\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(true, Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param columns   字段数组\n     * @return children\n     */\n    Children groupBy(boolean condition, List<R> columns);\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param columns 字段数组\n     * @return children\n     */\n    default Children groupBy(List<R> columns) {\n        return groupBy(true, columns);\n    }\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(\"id\", \"name\")</p>\n     *\n     * @param column  单个字段\n     * @param columns 字段数组\n     * @return children\n     */\n    default Children groupBy(R column, R... columns) {\n        return groupBy(true, column, columns);\n    }\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(true, \"id\", \"name\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @param columns   字段数组\n     * @return children\n     */\n    Children groupBy(boolean condition, R column, R... columns);\n\n    /**\n     * 分组：GROUP BY 字段, ...\n     * <p>例: groupBy(true, \"id\", Arrays.asList(\"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @param columns   字段数组\n     * @return children\n     * @since 3.5.4\n     */\n    Children groupBy(boolean condition, R column, List<R> columns);\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     * <p>例: orderByAsc(true, \"id\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @return children\n     */\n    default Children orderByAsc(boolean condition, R column) {\n        return orderBy(condition, true, column);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     * <p>例: orderByAsc(\"id\")</p>\n     *\n     * @param column 单个字段\n     * @return children\n     */\n    default Children orderByAsc(R column) {\n        return orderByAsc(true, column);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     * <p>例: orderByAsc(true, Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param columns   字段数组\n     * @return children\n     */\n    default Children orderByAsc(boolean condition, List<R> columns) {\n        return orderBy(condition, true, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     * <p>例: orderByAsc(Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param columns 字段数组\n     * @return children\n     */\n    default Children orderByAsc(List<R> columns) {\n        return orderByAsc(true, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     *\n     * @param column  字段\n     * @param columns 字段数组\n     * @return children\n     */\n    default Children orderByAsc(R column, R... columns) {\n        return orderByAsc(true, column, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @param columns   字段数组\n     */\n    default Children orderByAsc(boolean condition, R column, R... columns) {\n        return orderBy(condition, true, column, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... ASC\n     * <p>例: orderByAsc(true, Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param columns   字段数组\n     * @return children\n     * @since 3.5.4\n     */\n    default Children orderByAsc(boolean condition, R column, List<R> columns) {\n        return orderBy(condition, true, column, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     * <p>例: orderByDesc(true, \"id\")</p>\n     *\n     * @param condition 执行条件\n     * @param column    字段\n     * @return children\n     */\n    default Children orderByDesc(boolean condition, R column) {\n        return orderBy(condition, false, column);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     * <p>例: orderByDesc(\"id\")</p>\n     *\n     * @param column    字段\n     * @return children\n     */\n    default Children orderByDesc(R column) {\n        return orderByDesc(true, column);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     * <p>例: orderByDesc(true, Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param columns   字段列表\n     * @return children\n     */\n    default Children orderByDesc(boolean condition, List<R> columns) {\n        return orderBy(condition, false, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     *\n     * @param columns 字段列表\n     */\n    default Children orderByDesc(List<R> columns) {\n        return orderByDesc(true, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     *\n     * @param column  单个字段\n     * @param columns 字段列表\n     */\n    default Children orderByDesc(R column, R... columns) {\n        return orderByDesc(true, column, columns);\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @param columns   字段列表\n     */\n    default Children orderByDesc(boolean condition, R column, R... columns) {\n        return orderBy(condition, false, column, CollectionUtils.toList(columns));\n    }\n\n    /**\n     * 排序：ORDER BY 字段, ... DESC\n     *\n     * @param condition 执行条件\n     * @param column    单个字段\n     * @param columns   字段列表\n     * @since 3.5.4\n     */\n    default Children orderByDesc(boolean condition, R column, List<R> columns) {\n        return orderBy(condition, false, column, columns);\n    }\n\n\n    /**\n     * 排序：ORDER BY 字段, ...\n     * <p>例: orderBy(true, \"id\")</p>\n     *\n     * @param condition 执行条件\n     * @param isAsc     是否是 ASC 排序\n     * @param column    单个字段\n     * @return children\n     */\n    Children orderBy(boolean condition, boolean isAsc, R column);\n\n    /**\n     * 排序：ORDER BY 字段, ...\n     * <p>例: orderBy(true, Arrays.asList(\"id\", \"name\"))</p>\n     *\n     * @param condition 执行条件\n     * @param isAsc     是否是 ASC 排序\n     * @param columns   字段列表\n     * @return children\n     */\n    Children orderBy(boolean condition, boolean isAsc, List<R> columns);\n\n    /**\n     * 排序：ORDER BY 字段, ...\n     *\n     * @param condition 执行条件\n     * @param isAsc     是否是 ASC 排序\n     * @param columns   字段列表\n     * @return children\n     */\n    Children orderBy(boolean condition, boolean isAsc, R column, R... columns);\n\n    /**\n     * 排序：ORDER BY 字段, ...\n     *\n     * @param condition 执行条件\n     * @param isAsc     是否是 ASC 排序\n     * @param columns   字段列表\n     * @return children\n     * @since 3.5.4\n     */\n    Children orderBy(boolean condition, boolean isAsc, R column, List<R> columns);\n\n\n    /**\n     * HAVING ( sql语句 )\n     * <p>例1: having(\"sum(age) &gt; 10\")</p>\n     * <p>例2: having(\"sum(age) &gt; {0}\", 10)</p>\n     *\n     * @param sqlHaving sql 语句\n     * @param params    参数数组\n     * @return children\n     */\n    default Children having(String sqlHaving, Object... params) {\n        return having(true, sqlHaving, params);\n    }\n\n    /**\n     * HAVING ( sql语句 )\n     * <p>例1: having(true, \"sum(age) &gt; 10\")</p>\n     * <p>例2: having(true, \"sum(age) &gt; {0}\", 10)</p>\n     *\n     * @param condition 执行条件\n     * @param sqlHaving sql 语句\n     * @param params    参数数组\n     * @return children\n     */\n    Children having(boolean condition, String sqlHaving, Object... params);\n\n    /**\n     * 消费函数\n     *\n     * @param consumer 消费函数\n     * @return children\n     */\n    default Children func(Consumer<Children> consumer) {\n        return func(true, consumer);\n    }\n\n    /**\n     * 消费函数\n     *\n     * @param condition 执行条件\n     * @param consumer  消费函数\n     * @return children\n     * @since 3.3.1\n     */\n    Children func(boolean condition, Consumer<Children> consumer);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Join.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.interfaces;\n\nimport java.io.Serializable;\n\n/**\n * 查询条件封装\n * <p>拼接</p>\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\npublic interface Join<Children> extends Serializable {\n\n    /**\n     * 拼接 OR\n     *\n     * @return children\n     */\n    default Children or() {\n        return or(true);\n    }\n\n    /**\n     * 拼接 OR\n     *\n     * @param condition 执行条件\n     * @return children\n     */\n    Children or(boolean condition);\n\n    /**\n     * 拼接 sql\n     * <p>!! 会有 sql 注入风险 !!</p>\n     * <p>例1: apply(\"id = 1\")</p>\n     * <p>例2: apply(\"date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'\")</p>\n     * <p>例3: apply(\"date_format(dateColumn,'%Y-%m-%d') = {0}\", LocalDate.now())</p>\n     * <p>例4: apply(\"type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}\", \"待处理字符串\")</p>\n     *\n     * @param values    数据数组\n     * @return children\n     */\n    default Children apply(String applySql, Object... values) {\n        return apply(true, applySql, values);\n    }\n\n    /**\n     * 拼接 sql\n     * <p>!! 会有 sql 注入风险 !!</p>\n     * <p>例1: apply(\"id = 1\")</p>\n     * <p>例2: apply(\"date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'\")</p>\n     * <p>例3: apply(\"date_format(dateColumn,'%Y-%m-%d') = {0}\", LocalDate.now())</p>\n     * <p>例4: apply(\"type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}\", \"待处理字符串\")</p>\n     *\n     * @param condition 执行条件\n     * @param values    数据数组\n     * @return children\n     */\n    Children apply(boolean condition, String applySql, Object... values);\n\n    /**\n     * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)\n     * <p>例: last(\"limit 1\")</p>\n     * <p>注意只能调用一次,多次调用以最后一次为准</p>\n     *\n     * @param lastSql   sql语句\n     * @return children\n     */\n    default Children last(String lastSql) {\n        return last(true, lastSql);\n    }\n\n    /**\n     * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)\n     * <p>例: last(\"limit 1\")</p>\n     * <p>注意只能调用一次,多次调用以最后一次为准</p>\n     *\n     * @param condition 执行条件\n     * @param lastSql   sql语句\n     * @return children\n     */\n    Children last(boolean condition, String lastSql);\n\n    /**\n     * sql 注释(会拼接在 sql 的最后面)\n     * <p>\n     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}\n     * </p>\n     *\n     * @param comment sql注释\n     * @return children\n     */\n    default Children comment(String comment) {\n        return comment(true, comment);\n    }\n\n    /**\n     * sql 注释(会拼接在 sql 的最后面)\n     * <p>\n     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param comment   sql注释\n     * @return children\n     */\n    Children comment(boolean condition, String comment);\n\n    /**\n     * sql 起始句（会拼接在SQL语句的起始处）\n     * <p>\n     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}\n     * </p>\n     *\n     * @param firstSql 起始语句\n     * @return children\n     */\n    default Children first(String firstSql) {\n        return first(true, firstSql);\n    }\n\n    /**\n     * sql 起始句（会拼接在SQL语句的起始处）\n     * <p>\n     * 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param firstSql  起始语句\n     * @return children\n     * @since 3.3.1\n     */\n    Children first(boolean condition, String firstSql);\n\n    /**\n     * 拼接 EXISTS ( sql语句 )\n     * <p>!! sql 注入方法 !!</p>\n     * <p>例: exists(\"select id from table where age = 1\")</p>\n     *\n     * @param existsSql sql语句\n     * @param values    数据数组\n     * @return children\n     */\n    default Children exists(String existsSql, Object... values) {\n        return exists(true, existsSql, values);\n    }\n\n    /**\n     * 拼接 EXISTS ( sql语句 )\n     * <p>!! sql 注入方法 !!</p>\n     * <p>例: exists(\"select id from table where age = 1\")</p>\n     *\n     * @param condition 执行条件\n     * @param existsSql sql语句\n     * @param values    数据数组\n     * @return children\n     */\n    Children exists(boolean condition, String existsSql, Object... values);\n\n    /**\n     * 拼接 NOT EXISTS ( sql语句 )\n     * <p>!! sql 注入方法 !!</p>\n     * <p>例: notExists(\"select id from table where age = 1\")</p>\n     *\n     * @param existsSql sql语句\n     * @param values    数据数组\n     * @return children\n     */\n    default Children notExists(String existsSql, Object... values) {\n        return notExists(true, existsSql, values);\n    }\n\n    /**\n     * 拼接 NOT EXISTS ( sql语句 )\n     * <p>!! sql 注入方法 !!</p>\n     * <p>例: notExists(\"select id from table where age = 1\")</p>\n     *\n     * @param condition 执行条件\n     * @param existsSql sql语句\n     * @param values    数据数组\n     * @return children\n     */\n    Children notExists(boolean condition, String existsSql, Object... values);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/Nested.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.interfaces;\n\nimport java.io.Serializable;\nimport java.util.function.Consumer;\n\n/**\n * 查询条件封装\n * <p>嵌套</p>\n * <li>泛型 Param 是具体需要运行函数的类(也是 wrapper 的子类)</li>\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\npublic interface Nested<Param, Children> extends Serializable {\n\n    /**\n     * AND 嵌套\n     * <p>\n     * 例: and(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param consumer  消费函数\n     * @return children\n     */\n    default Children and(Consumer<Param> consumer) {\n        return and(true, consumer);\n    }\n\n    /**\n     * AND 嵌套\n     * <p>\n     * 例: and(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param consumer  消费函数\n     * @return children\n     */\n    Children and(boolean condition, Consumer<Param> consumer);\n\n    /**\n     * OR 嵌套\n     * <p>\n     * 例: or(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param consumer  消费函数\n     * @return children\n     */\n    default Children or(Consumer<Param> consumer) {\n        return or(true, consumer);\n    }\n\n    /**\n     * OR 嵌套\n     * <p>\n     * 例: or(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param consumer  消费函数\n     * @return children\n     */\n    Children or(boolean condition, Consumer<Param> consumer);\n\n    /**\n     * 正常嵌套 不带 AND 或者 OR\n     * <p>\n     * 例: nested(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param consumer  消费函数\n     * @return children\n     */\n    default Children nested(Consumer<Param> consumer) {\n        return nested(true, consumer);\n    }\n\n    /**\n     * 正常嵌套 不带 AND 或者 OR\n     * <p>\n     * 例: nested(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param consumer  消费函数\n     * @return children\n     */\n    Children nested(boolean condition, Consumer<Param> consumer);\n\n    /**\n     * not嵌套\n     * <p>\n     * 例: not(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param consumer  消费函数\n     * @return children\n     */\n    default Children not(Consumer<Param> consumer) {\n        return not(true, consumer);\n    }\n\n    /**\n     * not嵌套\n     * <p>\n     * 例: not(i -&gt; i.eq(\"name\", \"李白\").ne(\"status\", \"活着\"))\n     * </p>\n     *\n     * @param condition 执行条件\n     * @param consumer  消费函数\n     * @return children\n     */\n    Children not(boolean condition, Consumer<Param> consumer);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/interfaces/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Wrapper 接口\n */\npackage com.baomidou.mybatisplus.core.conditions.interfaces;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 条件构造区域，测试是否可以提交\n */\npackage com.baomidou.mybatisplus.core.conditions;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/LambdaQueryWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.query;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;\nimport com.baomidou.mybatisplus.core.conditions.SharedString;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\n\n/**\n * Lambda 语法使用 Wrapper\n *\n * @author hubin miemie HCL\n * @since 2017-05-26\n */\npublic class LambdaQueryWrapper<T> extends AbstractLambdaWrapper<T, LambdaQueryWrapper<T>>\n    implements Query<LambdaQueryWrapper<T>, T, SFunction<T, ?>> {\n\n    /**\n     * 查询字段\n     */\n    private SharedString sqlSelect = new SharedString();\n\n    public LambdaQueryWrapper() {\n        this((T) null);\n    }\n\n    public LambdaQueryWrapper(T entity) {\n        super.setEntity(entity);\n        super.initNeed();\n    }\n\n    public LambdaQueryWrapper(Class<T> entityClass) {\n        super.setEntityClass(entityClass);\n        super.initNeed();\n    }\n\n    LambdaQueryWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,\n                       Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,\n                       SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {\n        super.setEntity(entity);\n        super.setEntityClass(entityClass);\n        this.paramNameSeq = paramNameSeq;\n        this.paramNameValuePairs = paramNameValuePairs;\n        this.expression = mergeSegments;\n        this.sqlSelect = sqlSelect;\n        this.paramAlias = paramAlias;\n        this.lastSql = lastSql;\n        this.sqlComment = sqlComment;\n        this.sqlFirst = sqlFirst;\n    }\n\n    @Override\n    public LambdaQueryWrapper<T> select(boolean condition, List<SFunction<T, ?>> columns) {\n        return doSelect(condition, columns);\n    }\n\n    /**\n     * 过滤查询的字段信息(主键除外!)\n     * <p>例1: 只要 java 字段名以 \"test\" 开头的             -> select(i -&gt; i.getProperty().startsWith(\"test\"))</p>\n     * <p>例2: 只要 java 字段属性是 CharSequence 类型的     -> select(TableFieldInfo::isCharSequence)</p>\n     * <p>例3: 只要 java 字段没有填充策略的                 -> select(i -&gt; i.getFieldFill() == FieldFill.DEFAULT)</p>\n     * <p>例4: 要全部字段                                   -> select(i -&gt; true)</p>\n     * <p>例5: 只要主键字段                                 -> select(i -&gt; false)</p>\n     *\n     * @param predicate 过滤方式\n     * @return this\n     */\n    @Override\n    public LambdaQueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {\n        if (entityClass == null) {\n            entityClass = getEntityClass();\n        } else {\n            setEntityClass(entityClass);\n        }\n        Assert.notNull(entityClass, \"entityClass can not be null\");\n        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate));\n        return typedThis;\n    }\n\n    @Override\n    @SafeVarargs\n    public final LambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {\n        return doSelect(true, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final LambdaQueryWrapper<T> select(boolean condition, SFunction<T, ?>... columns) {\n        return doSelect(condition, CollectionUtils.toList(columns));\n    }\n\n    /**\n     * @since 3.5.4\n     */\n    protected LambdaQueryWrapper<T> doSelect(boolean condition, List<SFunction<T, ?>> columns) {\n        if (condition && CollectionUtils.isNotEmpty(columns)) {\n            this.sqlSelect.setStringValue(columnsToString(false, columns));\n        }\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSelect() {\n        return sqlSelect.getStringValue();\n    }\n\n    /**\n     * 用于生成嵌套 sql\n     * <p>故 sqlSelect 不向下传递</p>\n     */\n    @Override\n    protected LambdaQueryWrapper<T> instance() {\n        return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,\n            new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        sqlSelect.toNull();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/Query.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.query;\n\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * @author miemie\n * @since 2018-12-12\n */\npublic interface Query<Children, T, R> extends Serializable {\n\n    /**\n     * 指定查询字段\n     *\n     * @param columns 字段列表\n     * @return children\n     */\n    @SuppressWarnings(\"unchecked\")\n    default Children select(R... columns) {\n        return select(true, columns);\n    }\n\n    /**\n     * 指定查询字段\n     *\n     * @param condition 执行条件\n     * @param columns   字段列表\n     * @return children\n     */\n    @SuppressWarnings(\"unchecked\")\n    default Children select(boolean condition, R... columns) {\n        return select(condition, Arrays.asList(columns));\n    }\n\n    /**\n     * 指定查询字段\n     *\n     * @param columns   字段列表\n     * @return children\n     */\n    default Children select(List<R> columns) {\n        return select(true, columns);\n    }\n\n    /**\n     * 指定查询字段\n     *\n     * @param condition 执行条件\n     * @param columns   字段列表\n     * @return children\n     */\n    Children select(boolean condition, List<R> columns);\n\n    /**\n     * 过滤查询的字段信息(主键除外!)\n     * <p>注意只有内部有 entity 才能使用该方法</p>\n     */\n    default Children select(Predicate<TableFieldInfo> predicate) {\n        return select(null, predicate);\n    }\n\n    /**\n     * 过滤查询的字段信息(主键除外!)\n     * <p>例1: 只要 java 字段名以 \"test\" 开头的             -> select(i -> i.getProperty().startsWith(\"test\"))</p>\n     * <p>例2: 只要 java 字段属性是 CharSequence 类型的     -> select(TableFieldInfo::isCharSequence)</p>\n     * <p>例3: 只要 java 字段没有填充策略的                 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)</p>\n     * <p>例4: 要全部字段                                   -> select(i -> true)</p>\n     * <p>例5: 只要主键字段                                 -> select(i -> false)</p>\n     *\n     * @param predicate 过滤方式\n     * @return children\n     */\n    Children select(Class<T> entityClass, Predicate<TableFieldInfo> predicate);\n\n    /**\n     * 查询条件 SQL 片段\n     */\n    String getSqlSelect();\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/QueryWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.query;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper;\nimport com.baomidou.mybatisplus.core.conditions.SharedString;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\n\n/**\n * Entity 对象封装操作类\n *\n * @author hubin miemie HCL\n * @since 2018-05-25\n */\npublic class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>>\n    implements Query<QueryWrapper<T>, T, String> {\n\n    /**\n     * 查询字段\n     */\n    protected final SharedString sqlSelect = new SharedString();\n\n    public QueryWrapper() {\n        this((T) null);\n    }\n\n    public QueryWrapper(T entity) {\n        super.setEntity(entity);\n        super.initNeed();\n    }\n\n    public QueryWrapper(Class<T> entityClass) {\n        super.setEntityClass(entityClass);\n        super.initNeed();\n    }\n\n    public QueryWrapper(T entity, String... columns) {\n        super.setEntity(entity);\n        super.initNeed();\n        this.select(columns);\n    }\n\n    /**\n     * 非对外公开的构造方法,只用于生产嵌套 sql\n     *\n     * @param entityClass 本不应该需要的\n     */\n    private QueryWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,\n                         Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,\n                         SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {\n        super.setEntity(entity);\n        super.setEntityClass(entityClass);\n        this.paramNameSeq = paramNameSeq;\n        this.paramNameValuePairs = paramNameValuePairs;\n        this.expression = mergeSegments;\n        this.paramAlias = paramAlias;\n        this.lastSql = lastSql;\n        this.sqlComment = sqlComment;\n        this.sqlFirst = sqlFirst;\n    }\n\n\n    /**\n     * 检查 SQL 注入过滤\n     */\n    private boolean checkSqlInjection;\n\n    /**\n     * 开启检查 SQL 注入\n     */\n    public QueryWrapper<T> checkSqlInjection() {\n        this.checkSqlInjection = true;\n        return this;\n    }\n\n    @Override\n    protected String columnToString(String column) {\n        if (checkSqlInjection && SqlInjectionUtils.check(column)) {\n            throw new MybatisPlusException(\"Discovering SQL injection column: \" + column);\n        }\n        return column;\n    }\n\n    @Override\n    public QueryWrapper<T> select(boolean condition, List<String> columns) {\n        if (condition && CollectionUtils.isNotEmpty(columns)) {\n            this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns));\n        }\n        return typedThis;\n    }\n\n    @Override\n    public QueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {\n        super.setEntityClass(entityClass);\n        this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate));\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSelect() {\n        return sqlSelect.getStringValue();\n    }\n\n    /**\n     * 返回一个支持 lambda 函数写法的 wrapper\n     */\n    public LambdaQueryWrapper<T> lambda() {\n        return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), sqlSelect, paramNameSeq, paramNameValuePairs,\n            expression, paramAlias, lastSql, sqlComment, sqlFirst);\n    }\n\n    /**\n     * 用于生成嵌套 sql\n     * <p>\n     * 故 sqlSelect 不向下传递\n     * </p>\n     */\n    @Override\n    protected QueryWrapper<T> instance() {\n        return new QueryWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(),\n            paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        sqlSelect.toNull();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/query/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 查询 Wrapper\n */\npackage com.baomidou.mybatisplus.core.conditions.query;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/AbstractISegmentList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * SQL 片段集合 处理的抽象类\n *\n * @author miemie\n * @since 2018-06-27\n */\npublic abstract class AbstractISegmentList extends ArrayList<ISqlSegment> implements ISqlSegment, StringPool {\n\n    /**\n     * 最后一个值\n     */\n    ISqlSegment lastValue = null;\n    /**\n     * 刷新 lastValue\n     */\n    boolean flushLastValue = false;\n    /**\n     * 结果集缓存\n     */\n    private String sqlSegment = EMPTY;\n    /**\n     * 是否缓存过结果集\n     */\n    private boolean cacheSqlSegment = true;\n\n    /**\n     * 重写方法,做个性化适配\n     *\n     * @param c 元素集合\n     * @return 是否添加成功\n     */\n    @Override\n    public boolean addAll(Collection<? extends ISqlSegment> c) {\n        List<ISqlSegment> list = new ArrayList<>(c);\n        boolean goon = transformList(list, list.get(0), list.get(list.size() - 1));\n        if (goon) {\n            cacheSqlSegment = false;\n            if (flushLastValue) {\n                this.flushLastValue(list);\n            }\n            return super.addAll(list);\n        }\n        return false;\n    }\n\n    /**\n     * 在其中对值进行判断以及更改 list 的内部元素\n     *\n     * @param list         传入进来的 ISqlSegment 集合\n     * @param firstSegment ISqlSegment 集合里第一个值\n     * @param lastSegment  ISqlSegment 集合里最后一个值\n     * @return true 是否继续向下执行; false 不再向下执行\n     */\n    protected abstract boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment);\n\n    /**\n     * 刷新属性 lastValue\n     */\n    private void flushLastValue(List<ISqlSegment> list) {\n        lastValue = list.get(list.size() - 1);\n    }\n\n    /**\n     * 删除元素里最后一个值</br>\n     * 并刷新属性 lastValue\n     */\n    void removeAndFlushLast() {\n        remove(size() - 1);\n        flushLastValue(this);\n    }\n\n    @Override\n    public String getSqlSegment() {\n        if (cacheSqlSegment) {\n            return sqlSegment;\n        }\n        cacheSqlSegment = true;\n        sqlSegment = childrenSqlSegment();\n        return sqlSegment;\n    }\n\n    /**\n     * 只有该类进行过 addAll 操作,才会触发这个方法\n     * <p>\n     * 方法内可以放心进行操作\n     *\n     * @return sqlSegment\n     */\n    protected abstract String childrenSqlSegment();\n\n    @Override\n    public void clear() {\n        super.clear();\n        lastValue = null;\n        sqlSegment = EMPTY;\n        cacheSqlSegment = true;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/ColumnSegment.java",
    "content": "/*\r\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *     http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\npackage com.baomidou.mybatisplus.core.conditions.segments;\r\n\r\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\r\n\r\n@FunctionalInterface\r\npublic interface ColumnSegment extends ISqlSegment {\r\n}\r\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/GroupBySegmentList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\n\nimport java.util.List;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.GROUP_BY;\nimport static java.util.stream.Collectors.joining;\n\n/**\n * Group By SQL 片段\n *\n * @author miemie\n * @since 2018-06-27\n */\npublic class GroupBySegmentList extends AbstractISegmentList {\n\n    @Override\n    protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {\n        list.remove(0);\n        return true;\n    }\n\n    @Override\n    protected String childrenSqlSegment() {\n        if (isEmpty()) {\n            return EMPTY;\n        }\n        return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(COMMA, SPACE + GROUP_BY.getSqlSegment() + SPACE, EMPTY));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/HavingSegmentList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\n\nimport java.util.List;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.HAVING;\nimport static java.util.stream.Collectors.joining;\n\n/**\n * Having SQL 片段\n *\n * @author miemie\n * @since 2018-06-27\n */\npublic class HavingSegmentList extends AbstractISegmentList {\n\n    @Override\n    protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {\n        if (!isEmpty()) {\n            this.add(SqlKeyword.AND);\n        }\n        list.remove(0);\n        return true;\n    }\n\n    @Override\n    protected String childrenSqlSegment() {\n        if (isEmpty()) {\n            return EMPTY;\n        }\n        return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(SPACE, SPACE + HAVING.getSqlSegment() + SPACE, EMPTY));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/MatchSegment.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\nimport com.baomidou.mybatisplus.core.enums.WrapperKeyword;\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\nimport java.util.function.Predicate;\n\n/**\n * 匹配片段\n *\n * @author miemie\n * @since 2018-06-27\n */\n@Getter\n@AllArgsConstructor(access = AccessLevel.PACKAGE)\npublic enum MatchSegment {\n    GROUP_BY(i -> i == SqlKeyword.GROUP_BY),\n    ORDER_BY(i -> i == SqlKeyword.ORDER_BY),\n    NOT(i -> i == SqlKeyword.NOT),\n    AND(i -> i == SqlKeyword.AND),\n    OR(i -> i == SqlKeyword.OR),\n    AND_OR(i -> i == SqlKeyword.AND || i == SqlKeyword.OR),\n    EXISTS(i -> i == SqlKeyword.EXISTS),\n    HAVING(i -> i == SqlKeyword.HAVING),\n    APPLY(i -> i == WrapperKeyword.APPLY);\n\n    private final Predicate<ISqlSegment> predicate;\n\n    public boolean match(ISqlSegment segment) {\n        return getPredicate().test(segment);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/MergeSegments.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.AccessLevel;\nimport lombok.Getter;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 合并 SQL 片段\n *\n * @author miemie\n * @since 2018-06-27\n */\n@Getter\npublic class MergeSegments implements ISqlSegment {\n\n    private final NormalSegmentList normal = new NormalSegmentList();\n    private final GroupBySegmentList groupBy = new GroupBySegmentList();\n    private final HavingSegmentList having = new HavingSegmentList();\n    private final OrderBySegmentList orderBy = new OrderBySegmentList();\n\n    @Getter(AccessLevel.NONE)\n    private String sqlSegment = StringPool.EMPTY;\n    @Getter(AccessLevel.NONE)\n    private boolean cacheSqlSegment = true;\n\n    public void add(ISqlSegment... iSqlSegments) {\n        List<ISqlSegment> list = Arrays.asList(iSqlSegments);\n        ISqlSegment firstSqlSegment = list.get(0);\n        if (MatchSegment.ORDER_BY.match(firstSqlSegment)) {\n            orderBy.addAll(list);\n        } else if (MatchSegment.GROUP_BY.match(firstSqlSegment)) {\n            groupBy.addAll(list);\n        } else if (MatchSegment.HAVING.match(firstSqlSegment)) {\n            having.addAll(list);\n        } else {\n            normal.addAll(list);\n        }\n        cacheSqlSegment = false;\n    }\n\n    @Override\n    public String getSqlSegment() {\n        if (cacheSqlSegment) {\n            return sqlSegment;\n        }\n        cacheSqlSegment = true;\n        if (normal.isEmpty()) {\n            if (!groupBy.isEmpty() || !orderBy.isEmpty()) {\n                sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();\n            }\n        } else {\n            sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();\n        }\n        return sqlSegment;\n    }\n\n    /**\n     * 清理\n     *\n     * @since 3.3.1\n     */\n    public void clear() {\n        sqlSegment = StringPool.EMPTY;\n        cacheSqlSegment = true;\n        normal.clear();\n        groupBy.clear();\n        having.clear();\n        orderBy.clear();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/NormalSegmentList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 普通片段\n *\n * @author miemie\n * @since 2018-06-27\n */\npublic class NormalSegmentList extends AbstractISegmentList {\n\n    /**\n     * 是否处理了的上个 not\n     */\n    private boolean executeNot = true;\n\n    NormalSegmentList() {\n        this.flushLastValue = true;\n    }\n\n    @Override\n    protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {\n        if (list.size() == 1) {\n            /* 只有 and() 以及 or() 以及 not() 会进入 */\n            if (!MatchSegment.NOT.match(firstSegment)) {\n                //不是 not\n                if (isEmpty()) {\n                    //sqlSegment是 and 或者 or 并且在第一位,不继续执行\n                    return false;\n                }\n                boolean matchLastAnd = MatchSegment.AND.match(lastValue);\n                boolean matchLastOr = MatchSegment.OR.match(lastValue);\n                if (matchLastAnd || matchLastOr) {\n                    //上次最后一个值是 and 或者 or\n                    if (matchLastAnd && MatchSegment.AND.match(firstSegment)) {\n                        return false;\n                    } else if (matchLastOr && MatchSegment.OR.match(firstSegment)) {\n                        return false;\n                    } else {\n                        //和上次的不一样\n                        removeAndFlushLast();\n                    }\n                }\n            } else {\n                executeNot = false;\n                return false;\n            }\n        } else {\n            if (MatchSegment.APPLY.match(firstSegment)) {\n                list.remove(0);\n            }\n            if (!MatchSegment.AND_OR.match(lastValue) && !isEmpty()) {\n                add(SqlKeyword.AND);\n            }\n            if (!executeNot) {\n                list.add(0, SqlKeyword.NOT);\n                executeNot = true;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    protected String childrenSqlSegment() {\n        if (MatchSegment.AND_OR.match(lastValue)) {\n            removeAndFlushLast();\n        }\n        final String str = this.stream().map(ISqlSegment::getSqlSegment).collect(Collectors.joining(SPACE));\n        return (LEFT_BRACKET + str + RIGHT_BRACKET);\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        flushLastValue = true;\n        executeNot = true;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/OrderBySegmentList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.baomidou.mybatisplus.core.enums.SqlKeyword.ORDER_BY;\nimport static java.util.stream.Collectors.joining;\n\n/**\n * Order By SQL 片段\n *\n * @author miemie\n * @since 2018-06-27\n */\npublic class OrderBySegmentList extends AbstractISegmentList {\n\n    @Override\n    protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {\n        list.remove(0);\n        final List<ISqlSegment> sqlSegmentList = new ArrayList<>(list);\n        list.clear();\n        list.add(() -> sqlSegmentList.stream().map(ISqlSegment::getSqlSegment).collect(joining(SPACE)));\n        return true;\n    }\n\n    @Override\n    protected String childrenSqlSegment() {\n        if (isEmpty()) {\n            return EMPTY;\n        }\n        return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(COMMA, SPACE + ORDER_BY.getSqlSegment() + SPACE, EMPTY));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/segments/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * SQL 片段相关类\n */\npackage com.baomidou.mybatisplus.core.conditions.segments;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/LambdaUpdateWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.update;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;\nimport com.baomidou.mybatisplus.core.conditions.SharedString;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Lambda 更新封装\n *\n * @author hubin miemie HCL\n * @since 2018-05-30\n */\npublic class LambdaUpdateWrapper<T> extends AbstractLambdaWrapper<T, LambdaUpdateWrapper<T>>\n    implements Update<LambdaUpdateWrapper<T>, SFunction<T, ?>> {\n\n    /**\n     * SQL 更新字段内容，例如：name='1', age=2\n     */\n    private final List<String> sqlSet;\n\n    public LambdaUpdateWrapper() {\n        // 如果无参构造函数，请注意实体 NULL 情况 SET 必须有否则 SQL 异常\n        this((T) null);\n    }\n\n    public LambdaUpdateWrapper(T entity) {\n        super.setEntity(entity);\n        super.initNeed();\n        this.sqlSet = new ArrayList<>();\n    }\n\n    public LambdaUpdateWrapper(Class<T> entityClass) {\n        super.setEntityClass(entityClass);\n        super.initNeed();\n        this.sqlSet = new ArrayList<>();\n    }\n\n    LambdaUpdateWrapper(T entity, Class<T> entityClass, List<String> sqlSet, AtomicInteger paramNameSeq,\n                        Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,\n                        SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {\n        super.setEntity(entity);\n        super.setEntityClass(entityClass);\n        this.sqlSet = sqlSet;\n        this.paramNameSeq = paramNameSeq;\n        this.paramNameValuePairs = paramNameValuePairs;\n        this.expression = mergeSegments;\n        this.paramAlias = paramAlias;\n        this.lastSql = lastSql;\n        this.sqlComment = sqlComment;\n        this.sqlFirst = sqlFirst;\n    }\n\n    @Override\n    public LambdaUpdateWrapper<T> set(boolean condition, SFunction<T, ?> column, Object val, String mapping) {\n        return maybeDo(condition, () -> {\n            String sql = formatParam(mapping, val);\n            sqlSet.add(columnToString(column) + Constants.EQUALS + sql);\n        });\n    }\n\n    @Override\n    public LambdaUpdateWrapper<T> setSql(boolean condition, String setSql, Object... params) {\n        return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> {\n            sqlSet.add(formatSqlMaybeWithParam(setSql, params));\n        });\n    }\n\n    @Override\n    public LambdaUpdateWrapper<T> setIncrBy(boolean condition, SFunction<T, ?> column, Number val) {\n        return maybeDo(condition, () -> {\n            String realColumn = columnToString(column);\n            sqlSet.add(String.format(\"%s=%s + %s\", realColumn, realColumn, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val));\n        });\n    }\n\n    @Override\n    public LambdaUpdateWrapper<T> setDecrBy(boolean condition, SFunction<T, ?> column, Number val) {\n        return maybeDo(condition, () -> {\n            String realColumn = columnToString(column);\n            sqlSet.add(String.format(\"%s=%s - %s\", realColumn, realColumn, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val));\n        });\n    }\n\n    @Override\n    public String getSqlSet() {\n        if (CollectionUtils.isEmpty(sqlSet)) {\n            return null;\n        }\n        return String.join(Constants.COMMA, sqlSet);\n    }\n\n    @Override\n    protected LambdaUpdateWrapper<T> instance() {\n        return new LambdaUpdateWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,\n            new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        sqlSet.clear();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/Update.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.update;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2018-12-12\n */\npublic interface Update<Children, R> extends Serializable {\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param column 字段\n     * @param val    值\n     * @return children\n     */\n    default Children set(R column, Object val) {\n        return set(true, column, val);\n    }\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param condition 是否加入 set\n     * @param column    字段\n     * @param val       值\n     * @return children\n     */\n    default Children set(boolean condition, R column, Object val) {\n        return set(condition, column, val, null);\n    }\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param column  字段\n     * @param val     值\n     * @param mapping 例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler\n     * @return children\n     */\n    default Children set(R column, Object val, String mapping) {\n        return set(true, column, val, mapping);\n    }\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param condition 是否加入 set\n     * @param column    字段\n     * @param val       值\n     * @param mapping   例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler\n     * @return children\n     */\n    Children set(boolean condition, R column, Object val, String mapping);\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param setSql set sql\n     *               例1: setSql(\"id=1\")\n     *               例2: setSql(\"dateColumn={0}\", LocalDate.now())\n     *               例4: setSql(\"type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}\", \"待处理字符串\")\n     * @return children\n     */\n    default Children setSql(String setSql, Object... params) {\n        return setSql(true, setSql, params);\n    }\n\n    /**\n     * 设置 更新 SQL 的 SET 片段\n     *\n     * @param condition 执行条件\n     * @param setSql    set sql\n     *                  例1: setSql(\"id=1\")\n     *                  例2: setSql(\"dateColumn={0}\", LocalDate.now())\n     *                  例4: setSql(\"type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}\", \"待处理字符串\")\n     * @return children\n     */\n    Children setSql(boolean condition, String setSql, Object... params);\n\n    /**\n     * 字段自增变量 val 值\n     *\n     * @param column 字段\n     * @param val    变量值 1 字段自增 + 1\n     */\n    default Children setIncrBy(R column, Number val) {\n        return setIncrBy(true, column, val);\n    }\n\n    /**\n     * 字段自增变量 val 值\n     *\n     * @param condition 是否加入 set\n     * @param column    字段\n     * @param val       变量值 1 字段自增 + 1\n     */\n    Children setIncrBy(boolean condition, R column, Number val);\n\n    /**\n     * 字段自减变量 val 值\n     *\n     * @param column 字段\n     * @param val    变量值 1 字段自减 - 1\n     */\n    default Children setDecrBy(R column, Number val) {\n        return setDecrBy(true, column, val);\n    }\n\n    /**\n     * 字段自减变量 val 值\n     *\n     * @param condition 是否加入 set\n     * @param column    字段\n     * @param val       变量值 1 字段自减 - 1\n     */\n    Children setDecrBy(boolean condition, R column, Number val);\n\n    /**\n     * 获取 更新 SQL 的 SET 片段\n     */\n    String getSqlSet();\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/UpdateWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.conditions.update;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper;\nimport com.baomidou.mybatisplus.core.conditions.SharedString;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Update 条件封装\n *\n * @author hubin miemie HCL\n * @since 2018-05-30\n */\npublic class UpdateWrapper<T> extends AbstractWrapper<T, String, UpdateWrapper<T>>\n    implements Update<UpdateWrapper<T>, String> {\n\n    /**\n     * SQL 更新字段内容，例如：name='1', age=2\n     */\n    private final List<String> sqlSet;\n\n    public UpdateWrapper() {\n        // 如果无参构造函数，请注意实体 NULL 情况 SET 必须有否则 SQL 异常\n        this(null);\n    }\n\n    public UpdateWrapper(T entity) {\n        super.setEntity(entity);\n        super.initNeed();\n        this.sqlSet = new ArrayList<>();\n    }\n\n    private UpdateWrapper(T entity, List<String> sqlSet, AtomicInteger paramNameSeq,\n                          Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,\n                          SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {\n        super.setEntity(entity);\n        this.sqlSet = sqlSet;\n        this.paramNameSeq = paramNameSeq;\n        this.paramNameValuePairs = paramNameValuePairs;\n        this.expression = mergeSegments;\n        this.paramAlias = paramAlias;\n        this.lastSql = lastSql;\n        this.sqlComment = sqlComment;\n        this.sqlFirst = sqlFirst;\n    }\n\n\n    /**\n     * 检查 SQL 注入过滤\n     */\n    private boolean checkSqlInjection;\n\n    /**\n     * 开启检查 SQL 注入\n     */\n    public UpdateWrapper<T> checkSqlInjection() {\n        this.checkSqlInjection = true;\n        return this;\n    }\n\n    @Override\n    protected String columnToString(String column) {\n        if (checkSqlInjection && SqlInjectionUtils.check(column)) {\n            throw new MybatisPlusException(\"Discovering SQL injection column: \" + column);\n        }\n        return column;\n    }\n\n    @Override\n    public String getSqlSet() {\n        if (CollectionUtils.isEmpty(sqlSet)) {\n            return null;\n        }\n        return String.join(Constants.COMMA, sqlSet);\n    }\n\n    @Override\n    public UpdateWrapper<T> set(boolean condition, String column, Object val, String mapping) {\n        return maybeDo(condition, () -> {\n            String sql = formatParam(mapping, val);\n            sqlSet.add(column + Constants.EQUALS + sql);\n        });\n    }\n\n    @Override\n    public UpdateWrapper<T> setSql(boolean condition, String setSql, Object... params) {\n        return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> {\n            sqlSet.add(formatSqlMaybeWithParam(setSql, params));\n        });\n    }\n\n    @Override\n    public UpdateWrapper<T> setIncrBy(boolean condition, String column, Number val) {\n        return maybeDo(condition, () -> {\n            sqlSet.add(String.format(\"%s=%s + %s\", column, column, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val));\n        });\n    }\n\n    @Override\n    public UpdateWrapper<T> setDecrBy(boolean condition, String column, Number val) {\n        return maybeDo(condition, () -> {\n            sqlSet.add(String.format(\"%s=%s - %s\", column, column, val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : val));\n        });\n    }\n\n    /**\n     * 返回一个支持 lambda 函数写法的 wrapper\n     */\n    public LambdaUpdateWrapper<T> lambda() {\n        return new LambdaUpdateWrapper<>(getEntity(), getEntityClass(), sqlSet, paramNameSeq, paramNameValuePairs,\n            expression, paramAlias, lastSql, sqlComment, sqlFirst);\n    }\n\n    @Override\n    protected UpdateWrapper<T> instance() {\n        return new UpdateWrapper<>(getEntity(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(),\n            paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        sqlSet.clear();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/conditions/update/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 更新 Wrapper\n */\npackage com.baomidou.mybatisplus.core.conditions.update;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/config/GlobalConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.config;\n\nimport com.baomidou.mybatisplus.annotation.FieldStrategy;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;\nimport com.baomidou.mybatisplus.core.injector.ISqlInjector;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.experimental.Accessors;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListSet;\n\n/**\n * Mybatis 全局缓存\n *\n * @author Caratacus\n * @since 2016-12-06\n */\n@Data\n@Accessors(chain = true)\npublic class GlobalConfig implements Serializable {\n    /**\n     * 是否开启 LOGO\n     */\n    private boolean banner = true;\n    /**\n     * 是否初始化 SqlRunner\n     */\n    private boolean enableSqlRunner = false;\n    /**\n     * 数据库相关配置\n     */\n    private DbConfig dbConfig;\n    /**\n     * SQL注入器\n     */\n    private ISqlInjector sqlInjector = new DefaultSqlInjector();\n    /**\n     * Mapper父类\n     */\n    private Class<?> superMapperClass = Mapper.class;\n    /**\n     * 仅用于缓存 SqlSessionFactory(外部勿进行set,set了也没用)\n     *\n     * @deprecated 3.5.3.2\n     */\n    @Deprecated\n    private SqlSessionFactory sqlSessionFactory;\n    /**\n     * 缓存已注入CRUD的Mapper信息\n     */\n    private Set<String> mapperRegistryCache = new ConcurrentSkipListSet<>();\n    /**\n     * 元对象字段填充控制器\n     */\n    private MetaObjectHandler metaObjectHandler;\n    /**\n     * 注解控制器\n     */\n    private AnnotationHandler annotationHandler = new AnnotationHandler() {\n    };\n    /**\n     * 参与 TableInfo 的初始化\n     */\n    private PostInitTableInfoHandler postInitTableInfoHandler = new PostInitTableInfoHandler() {\n    };\n    /**\n     * 主键生成器\n     */\n    private IdentifierGenerator identifierGenerator;\n    /**\n     * 数据库相关配置\n     */\n    private Sequence sequence = new Sequence();\n\n    @Data\n    public static class DbConfig {\n        /**\n         * 主键类型\n         */\n        private IdType idType = IdType.ASSIGN_ID;\n        /**\n         * 表名前缀\n         */\n        private String tablePrefix;\n        /**\n         * schema\n         *\n         * @since 3.1.1\n         */\n        private String schema;\n        /**\n         * db字段 format\n         * <p>\n         * 例: `%s`\n         * <p>\n         * 对主键无效\n         *\n         * @since 3.1.1\n         */\n        private String columnFormat;\n        /**\n         * db 表 format\n         * <p>\n         * 例: `%s`\n         * <p>\n         *\n         * @since 3.5.3.2\n         */\n        private String tableFormat;\n        /**\n         * entity 的字段(property)的 format,只有在 column as property 这种情况下生效\n         * <p>\n         * 例: `%s`\n         * <p>\n         * 对主键无效\n         *\n         * @since 3.3.0\n         */\n        private String propertyFormat;\n        /**\n         * 实验性功能,占位符替换,等同于 {@link com.baomidou.mybatisplus.extension.plugins.inner.ReplacePlaceholderInnerInterceptor},\n         * 只是这个属于启动时替换,用得地方多会启动慢一点点,不适用于其他的 {@link org.apache.ibatis.scripting.LanguageDriver}\n         *\n         * @since 3.4.2\n         */\n        private boolean replacePlaceholder;\n        /**\n         * 转义符\n         * <p>\n         * 配合 {@link #replacePlaceholder} 使用时有效\n         * <p>\n         * 例: \" 或 ' 或 `\n         *\n         * @since 3.4.2\n         */\n        private String escapeSymbol;\n        /**\n         * 表名是否使用驼峰转下划线命名,只对表名生效\n         */\n        private boolean tableUnderline = true;\n        /**\n         * 大写命名,对表名和字段名均生效\n         */\n        private boolean capitalMode = false;\n        /**\n         * 表主键生成器\n         */\n        private List<IKeyGenerator> keyGenerators;\n        /**\n         * 逻辑删除全局属性名\n         */\n        private String logicDeleteField;\n        /**\n         * 逻辑删除全局值（默认 1、表示已删除）\n         */\n        private String logicDeleteValue = \"1\";\n        /**\n         * 逻辑未删除全局值（默认 0、表示未删除）\n         */\n        private String logicNotDeleteValue = \"0\";\n        /**\n         * 字段验证策略之 insert\n         *\n         * @since 3.1.2\n         */\n        private FieldStrategy insertStrategy = FieldStrategy.NOT_NULL;\n        /**\n         * 字段验证策略之 update\n         *\n         * @since 3.1.2\n         */\n        private FieldStrategy updateStrategy = FieldStrategy.NOT_NULL;\n        /**\n         * 字段验证策略之 where\n         *\n         * @since 3.4.4\n         */\n        private FieldStrategy whereStrategy = FieldStrategy.NOT_NULL;\n        /**\n         * 生成INSERT语句时忽略自增主键字段(默认不忽略,主键有值时写入主键值,无值自增).\n         * <p>当设置为true时,执行生成SQL语句无论ID是否有值都会忽视 (此为3.4.3.1版本下策略,如果升级遇到问题可以考虑开启此配置来兼容升级)</p>\n         *\n         * @since 3.5.6\n         */\n        private boolean insertIgnoreAutoIncrementColumn = false;\n    }\n\n    /**\n     * 雪花ID配置\n     * <p>\n     * 1. 手动指定{@link #workerId} 和 {@link #datacenterId}\n     * </p>\n     * <p>\n     * 2. 基于网卡信息和进程PID计算 {@link #workerId} 和 {@link #datacenterId}\n     * </p>\n     *\n     * @since 3.5.7\n     */\n    @Getter\n    @Setter\n    public static class Sequence {\n\n        /**\n         * 工作机器 ID\n         */\n        private Long workerId;\n\n        /**\n         * 数据标识 ID 部分\n         */\n        private Long datacenterId;\n\n        /**\n         * 首选网络地址 (例如: 192.168.1,支持正则)\n         */\n        private List<String> preferredNetworks = new ArrayList<>();\n\n        /**\n         * 忽略网卡(例如:eth0,,支持正则)\n         */\n        private List<String> ignoredInterfaces = new ArrayList<>();\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/config/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 全局默认配置\n */\npackage com.baomidou.mybatisplus.core.config;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlKeyword.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.enums;\n\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.AllArgsConstructor;\n\n/**\n * SQL 保留关键字枚举\n *\n * @author hubin\n * @since 2018-05-28\n */\n@AllArgsConstructor\npublic enum SqlKeyword implements ISqlSegment {\n    AND(\"AND\"),\n    OR(\"OR\"),\n    NOT(\"NOT\"),\n    IN(\"IN\"),\n    NOT_IN(\"NOT IN\"),\n    LIKE(\"LIKE\"),\n    NOT_LIKE(\"NOT LIKE\"),\n    EQ(StringPool.EQUALS),\n    NE(\"<>\"),\n    GT(StringPool.RIGHT_CHEV),\n    GE(\">=\"),\n    LT(StringPool.LEFT_CHEV),\n    LE(\"<=\"),\n    IS_NULL(\"IS NULL\"),\n    IS_NOT_NULL(\"IS NOT NULL\"),\n    GROUP_BY(\"GROUP BY\"),\n    HAVING(\"HAVING\"),\n    ORDER_BY(\"ORDER BY\"),\n    EXISTS(\"EXISTS\"),\n    NOT_EXISTS(\"NOT EXISTS\"),\n    BETWEEN(\"BETWEEN\"),\n    NOT_BETWEEN(\"NOT BETWEEN\"),\n    ASC(\"ASC\"),\n    DESC(\"DESC\");\n\n    private final String keyword;\n\n    @Override\n    public String getSqlSegment() {\n        return this.keyword;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlLike.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.enums;\n\n/**\n * SQL like 枚举\n *\n * @author Caratacus\n * @since 2016-12-4\n */\npublic enum SqlLike {\n    /**\n     * %值\n     */\n    LEFT,\n    /**\n     * 值%\n     */\n    RIGHT,\n    /**\n     * %值%\n     */\n    DEFAULT\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/SqlMethod.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.enums;\n\n/**\n * MybatisPlus 支持 SQL 方法\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic enum SqlMethod {\n    /**\n     * 插入\n     */\n    INSERT_ONE(\"insert\", \"插入一条数据（选择字段插入）\", \"<script>\\nINSERT INTO %s %s VALUES %s\\n</script>\"),\n    UPSERT_ONE(\"upsert\", \"Phoenix插入一条数据（选择字段插入）\", \"<script>\\nUPSERT INTO %s %s VALUES %s\\n</script>\"),\n\n    /**\n     * 删除\n     */\n    DELETE_BY_ID(\"deleteById\", \"根据ID 删除一条数据\", \"DELETE FROM %s WHERE %s=#{%s}\"),\n    @Deprecated\n    DELETE_BY_MAP(\"deleteByMap\", \"根据columnMap 条件删除记录\", \"<script>\\nDELETE FROM %s %s\\n</script>\"),\n    DELETE(\"delete\", \"根据 entity 条件删除记录\", \"<script>\\nDELETE FROM %s %s %s\\n</script>\"),\n    /**\n     * @deprecated 3.5.7 {@link #DELETE_BY_IDS}\n     */\n    @Deprecated\n    DELETE_BATCH_BY_IDS(\"deleteBatchIds\", \"根据ID集合，批量删除数据\", \"<script>\\nDELETE FROM %s WHERE %s IN (%s)\\n</script>\"),\n    /**\n     * @since 3.5.7\n     */\n    DELETE_BY_IDS(\"deleteByIds\", \"根据ID集合，批量删除数据\", \"<script>\\nDELETE FROM %s WHERE %s IN (%s)\\n</script>\"),\n\n    /**\n     * 逻辑删除\n     */\n    LOGIC_DELETE_BY_ID(\"deleteById\", \"根据ID 逻辑删除一条数据\", \"<script>\\nUPDATE %s %s WHERE %s=#{%s} %s\\n</script>\"),\n    LOGIC_DELETE_BY_MAP(\"deleteByMap\", \"根据columnMap 条件逻辑删除记录\", \"<script>\\nUPDATE %s %s %s\\n</script>\"),\n    LOGIC_DELETE(\"delete\", \"根据 entity 条件逻辑删除记录\", \"<script>\\nUPDATE %s %s %s %s\\n</script>\"),\n    /**\n     * @deprecated 3.5.7 {@link #LOGIC_DELETE_BY_IDS}\n     */\n    @Deprecated\n    LOGIC_DELETE_BATCH_BY_IDS(\"deleteBatchIds\", \"根据ID集合，批量逻辑删除数据\", \"<script>\\nUPDATE %s %s WHERE %s IN (%s) %s\\n</script>\"),\n    /**\n     * @since 3.5.7\n     */\n    LOGIC_DELETE_BY_IDS(\"deleteByIds\", \"根据ID集合，批量逻辑删除数据\", \"<script>\\nUPDATE %s %s WHERE %s IN (%s) %s\\n</script>\"),\n\n    /**\n     * 修改\n     */\n    UPDATE_BY_ID(\"updateById\", \"根据ID 选择修改数据\", \"<script>\\nUPDATE %s %s WHERE %s=#{%s} %s\\n</script>\"),\n    UPDATE(\"update\", \"根据 whereEntity 条件，更新记录\", \"<script>\\nUPDATE %s %s %s %s\\n</script>\"),\n\n    /**\n     * 逻辑删除 -> 修改\n     */\n    LOGIC_UPDATE_BY_ID(\"updateById\", \"根据ID 修改数据\", \"<script>\\nUPDATE %s %s WHERE %s=#{%s} %s\\n</script>\"),\n\n    /**\n     * 查询\n     */\n    SELECT_BY_ID(\"selectById\", \"根据ID 查询一条数据\", \"SELECT %s FROM %s WHERE %s=#{%s} %s\"),\n    @Deprecated\n    SELECT_BY_MAP(\"selectByMap\", \"根据columnMap 查询一条数据\", \"<script>SELECT %s FROM %s %s\\n</script>\"),\n    /**\n     * @deprecated 3.5.8 {@link #SELECT_BY_IDS}\n     */\n    @Deprecated\n    SELECT_BATCH_BY_IDS(\"selectBatchIds\", \"根据ID集合，批量查询数据\", \"<script>SELECT %s FROM %s WHERE %s IN (%s) %s </script>\"),\n    /**\n     * @since 3.5.8\n     */\n    SELECT_BY_IDS(\"selectByIds\", \"根据ID集合，批量查询数据\", \"<script>SELECT %s FROM %s WHERE %s IN (%s) %s </script>\"),\n    @Deprecated\n    SELECT_ONE(\"selectOne\", \"查询满足条件一条数据\", \"<script>%s SELECT %s FROM %s %s %s\\n</script>\"),\n    SELECT_COUNT(\"selectCount\", \"查询满足条件总记录数\", \"<script>%s SELECT COUNT(%s) AS total FROM %s %s %s\\n</script>\"),\n    SELECT_LIST(\"selectList\", \"查询满足条件所有数据\", \"<script>%s SELECT %s FROM %s %s %s %s\\n</script>\"),\n    @Deprecated\n    SELECT_PAGE(\"selectPage\", \"查询满足条件所有数据（并翻页）\", \"<script>%s SELECT %s FROM %s %s %s %s\\n</script>\"),\n    SELECT_MAPS(\"selectMaps\", \"查询满足条件所有数据\", \"<script>%s SELECT %s FROM %s %s %s %s\\n</script>\"),\n    @Deprecated\n    SELECT_MAPS_PAGE(\"selectMapsPage\", \"查询满足条件所有数据（并翻页）\", \"<script>\\n%s SELECT %s FROM %s %s %s %s\\n</script>\"),\n    SELECT_OBJS(\"selectObjs\", \"查询满足条件所有数据\", \"<script>%s SELECT %s FROM %s %s %s %s\\n</script>\");\n\n    private final String method;\n    private final String desc;\n    private final String sql;\n\n    SqlMethod(String method, String desc, String sql) {\n        this.method = method;\n        this.desc = desc;\n        this.sql = sql;\n    }\n\n    public String format(Object... args) {\n        return String.format(sql, args);\n    }\n\n    public String getMethod() {\n        return method;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public String getSql() {\n        return sql;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/WrapperKeyword.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.enums;\n\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport lombok.AllArgsConstructor;\n\n/**\n * wrapper 内部使用枚举\n *\n * @author miemie\n * @since 2018-07-30\n */\n@AllArgsConstructor\npublic enum WrapperKeyword implements ISqlSegment {\n    /**\n     * 只用作于辨识,不用于其他\n     */\n    APPLY(null);\n\n    private final String keyword;\n\n    @Override\n    public String getSqlSegment() {\n        return keyword;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/enums/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 枚举相关\n */\npackage com.baomidou.mybatisplus.core.enums;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/exceptions/MybatisPlusException.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.exceptions;\n\nimport org.apache.ibatis.exceptions.PersistenceException;\n\n/**\n * MybatisPlus 异常类\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class MybatisPlusException extends PersistenceException {\n\n    private static final long serialVersionUID = 1L;\n\n    public MybatisPlusException(String message) {\n        super(message);\n    }\n\n    public MybatisPlusException(Throwable throwable) {\n        super(throwable);\n    }\n\n    public MybatisPlusException(String message, Throwable throwable) {\n        super(message, throwable);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/exceptions/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 异常\n */\npackage com.baomidou.mybatisplus.core.exceptions;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/AnnotationHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.AnnotationUtils;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\n/**\n * @author 唐振超\n * @since 2023-02-25\n */\npublic interface AnnotationHandler {\n\n    /**\n     * 从类上获取注解\n     *\n     * @param beanClass       类的class\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 注解\n     */\n    default <T extends Annotation> T getAnnotation(Class<?> beanClass, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass);\n    }\n\n    /**\n     * 判断类上是否存在注解\n     *\n     * @param beanClass       类的class\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 是否包含该注解\n     */\n    default <T extends Annotation> boolean isAnnotationPresent(Class<?> beanClass, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, beanClass) != null;\n    }\n\n    /**\n     * 从字段上获取注解\n     *\n     * @param field           字段\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 注解\n     */\n    default <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, field);\n    }\n\n    /**\n     * 判断字段上是否存在注解\n     *\n     * @param field           字段\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 是否包含该注解\n     */\n    default <T extends Annotation> boolean isAnnotationPresent(Field field, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, field) != null;\n    }\n\n    /**\n     * 从方法上获取注解\n     *\n     * @param method          方法\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 注解\n     */\n    default <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, method);\n    }\n\n    /**\n     * 判断方法上是否存在注解\n     *\n     * @param method           方法\n     * @param annotationClass 要获取的注解class\n     * @param <T>             具体注解\n     * @return 是否包含该注解\n     */\n    default <T extends Annotation> boolean isAnnotationPresent(Method method, Class<T> annotationClass) {\n        return AnnotationUtils.findFirstAnnotation(annotationClass, method) != null;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/CompositeEnumTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport org.apache.ibatis.type.EnumTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeException;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.lang.reflect.Constructor;\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author miemie\n * @since 2022-03-07\n */\npublic class CompositeEnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {\n\n    private static final Map<Class<?>, Boolean> MP_ENUM_CACHE = new ConcurrentHashMap<>();\n    private static Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;\n    private final TypeHandler<E> delegate;\n\n    public CompositeEnumTypeHandler(Class<E> enumClassType) {\n        if (enumClassType == null) {\n            throw new IllegalArgumentException(\"Type argument cannot be null\");\n        }\n        if (CollectionUtils.computeIfAbsent(MP_ENUM_CACHE, enumClassType, MybatisEnumTypeHandler::isMpEnums)) {\n            delegate = new MybatisEnumTypeHandler<>(enumClassType);\n        } else {\n            delegate = getInstance(enumClassType, defaultEnumTypeHandler);\n        }\n    }\n\n    public static void setDefaultEnumTypeHandler(Class<? extends TypeHandler> defaultEnumTypeHandler) {\n        if (defaultEnumTypeHandler != null && !MybatisEnumTypeHandler.class.isAssignableFrom(defaultEnumTypeHandler)) {\n            CompositeEnumTypeHandler.defaultEnumTypeHandler = defaultEnumTypeHandler;\n        }\n    }\n\n    @Override\n    public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {\n        delegate.setParameter(ps, i, parameter, jdbcType);\n    }\n\n    @Override\n    public E getResult(ResultSet rs, String columnName) throws SQLException {\n        return delegate.getResult(rs, columnName);\n    }\n\n    @Override\n    public E getResult(ResultSet rs, int columnIndex) throws SQLException {\n        return delegate.getResult(rs, columnIndex);\n    }\n\n    @Override\n    public E getResult(CallableStatement cs, int columnIndex) throws SQLException {\n        return delegate.getResult(cs, columnIndex);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {\n        if (javaTypeClass != null) {\n            try {\n                Constructor<?> c = typeHandlerClass.getConstructor(Class.class);\n                return (TypeHandler<T>) c.newInstance(javaTypeClass);\n            } catch (NoSuchMethodException ignored) {\n                // ignored\n            } catch (Exception e) {\n                throw new TypeException(\"Failed invoking constructor for handler \" + typeHandlerClass, e);\n            }\n        }\n        try {\n            Constructor<?> c = typeHandlerClass.getConstructor();\n            return (TypeHandler<T>) c.newInstance();\n        } catch (Exception e) {\n            throw new TypeException(\"Unable to find a usable constructor for \" + typeHandlerClass, e);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/IJsonTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\n\n/**\n * Json类型处理器接口(实现类确保为多例状态).\n * <p>\n * 注意:查询返回时需要使用resultMap\n *\n * <pre>\n * Example:\n *     &lt;result property=\"xx\" column=\"xx\" javaType=\"list\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/&gt;\n *     &lt;result property=\"xx\" column=\"xx\" typeHandler=\"com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler\"/&gt;\n * </pre>\n * </p>\n *\n * @author nieqiurong 2024年3月4日\n * @see TableName#autoResultMap() 自动构建\n * @see TableName#resultMap() 手动指定\n * @since 3.5.6\n */\npublic interface IJsonTypeHandler<T> {\n\n    /**\n     * 反序列化json\n     *\n     * @param json json字符串\n     * @return T\n     */\n    T parse(String json);\n\n    /**\n     * 序列化json\n     *\n     * @param obj 对象信息\n     * @return json字符串\n     */\n    String toJson(T obj);\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/MetaObjectHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.reflection.MetaObject;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\n/**\n * 元对象字段填充控制器抽象类，实现公共字段自动写入<p>\n * <p>\n * 所有入参的 MetaObject 必定是 entity 或其子类的 MetaObject\n *\n * @author hubin\n * @since 2016-08-28\n */\npublic interface MetaObjectHandler {\n\n    /**\n     * 是否开启了插入填充\n     *\n     * @deprecated 3.5.6 {@link #openInsertFill(MappedStatement)}\n     */\n    @Deprecated\n    default boolean openInsertFill() {\n        return true;\n    }\n\n    /**\n     * 是否开启插入填充\n     *\n     * @param mappedStatement {@link MappedStatement}\n     * @return 是否开启\n     * @since 3.5.6\n     */\n    default boolean openInsertFill(MappedStatement mappedStatement) {\n        return true;\n    }\n\n    /**\n     * 是否开启了更新填充\n     *\n     * @deprecated 3.5.6 {@link #openUpdateFill(MappedStatement)}\n     */\n    @Deprecated\n    default boolean openUpdateFill() {\n        return true;\n    }\n\n    /**\n     * 是否开启了更新填充\n     *\n     * @param mappedStatement {@link MappedStatement}\n     * @return 是否开启\n     * @since 3.5.6\n     */\n    default boolean openUpdateFill(MappedStatement mappedStatement) {\n        return true;\n    }\n\n    /**\n     * 插入元对象字段填充（用于插入时对公共字段的填充）\n     *\n     * @param metaObject 元对象\n     */\n    void insertFill(MetaObject metaObject);\n\n    /**\n     * 更新元对象字段填充（用于更新时对公共字段的填充）\n     *\n     * @param metaObject 元对象\n     */\n    void updateFill(MetaObject metaObject);\n\n    /**\n     * 通用填充\n     *\n     * @param fieldName  java bean property name\n     * @param fieldVal   java bean property value\n     * @param metaObject meta object parameter\n     */\n    default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {\n        if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {\n            metaObject.setValue(fieldName, fieldVal);\n        }\n        return this;\n    }\n\n    /**\n     * get value from java bean by propertyName\n     *\n     * @param fieldName  java bean property name\n     * @param metaObject parameter wrapper\n     * @return 字段值\n     */\n    default Object getFieldValByName(String fieldName, MetaObject metaObject) {\n        return metaObject.hasGetter(fieldName) ? metaObject.getValue(fieldName) : null;\n    }\n\n    /**\n     * find the tableInfo cache by metaObject </p>\n     * 获取 TableInfo 缓存\n     *\n     * @param metaObject meta object parameter\n     * @return TableInfo\n     * @since 3.3.0\n     */\n    default TableInfo findTableInfo(MetaObject metaObject) {\n        return TableInfoHelper.getTableInfo(metaObject.getOriginalObject().getClass());\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default <T, E extends T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {\n        return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default <T, E extends T> MetaObjectHandler strictInsertFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {\n        return strictInsertFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default MetaObjectHandler strictInsertFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {\n        return strictFill(true, tableInfo, metaObject, strictFills);\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default <T, E extends T> MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {\n        return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldVal, fieldType)));\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default <T, E extends T> MetaObjectHandler strictUpdateFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal) {\n        return strictUpdateFill(findTableInfo(metaObject), metaObject, Collections.singletonList(StrictFill.of(fieldName, fieldType, fieldVal)));\n    }\n\n    /**\n     * @param metaObject metaObject meta object parameter\n     * @return this\n     * @since 3.3.0\n     */\n    default MetaObjectHandler strictUpdateFill(TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {\n        return strictFill(false, tableInfo, metaObject, strictFills);\n    }\n\n    /**\n     * 严格填充,只针对非主键的字段,只有该表注解了fill 并且 字段名和字段属性 能匹配到才会进行填充(null 值不填充)\n     *\n     * @param insertFill  是否验证在 insert 时填充\n     * @param tableInfo   cache 缓存\n     * @param metaObject  metaObject meta object parameter\n     * @param strictFills 填充信息\n     * @return this\n     * @since 3.3.0\n     */\n    default MetaObjectHandler strictFill(boolean insertFill, TableInfo tableInfo, MetaObject metaObject, List<StrictFill<?, ?>> strictFills) {\n        if ((insertFill && tableInfo.isWithInsertFill()) || (!insertFill && tableInfo.isWithUpdateFill())) {\n            strictFills.forEach(i -> {\n                final String fieldName = i.getFieldName();\n                final Class<?> fieldType = i.getFieldType();\n                tableInfo.getFieldList().stream()\n                    .filter(j -> j.getProperty().equals(fieldName) && fieldType.equals(j.getPropertyType()) &&\n                        ((insertFill && j.isWithInsertFill()) || (!insertFill && j.isWithUpdateFill()))).findFirst()\n                    .ifPresent(j -> strictFillStrategy(metaObject, fieldName, i.getFieldVal()));\n            });\n        }\n        return this;\n    }\n\n    /**\n     * 填充策略,默认有值不覆盖,如果提供的值为null也不填充\n     *\n     * @param metaObject metaObject meta object parameter\n     * @param fieldName  java bean property name\n     * @param fieldVal   java bean property value of Supplier\n     * @return this\n     * @since 3.3.0\n     */\n    default MetaObjectHandler fillStrategy(MetaObject metaObject, String fieldName, Object fieldVal) {\n        if (getFieldValByName(fieldName, metaObject) == null) {\n            setFieldValByName(fieldName, fieldVal, metaObject);\n        }\n        return this;\n    }\n\n    /**\n     * 严格模式填充策略,默认有值不覆盖,如果提供的值为null也不填充\n     *\n     * @param metaObject metaObject meta object parameter\n     * @param fieldName  java bean property name\n     * @param fieldVal   java bean property value of Supplier\n     * @return this\n     * @since 3.3.0\n     */\n    default MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier<?> fieldVal) {\n        if (metaObject.getValue(fieldName) == null) {\n            Object obj = fieldVal.get();\n            if (Objects.nonNull(obj)) {\n                metaObject.setValue(fieldName, obj);\n            }\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/MybatisEnumTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.baomidou.mybatisplus.annotation.IEnum;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.reflection.DefaultReflectorFactory;\nimport org.apache.ibatis.reflection.MetaClass;\nimport org.apache.ibatis.reflection.ReflectorFactory;\nimport org.apache.ibatis.reflection.invoker.Invoker;\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 自定义枚举属性转换器\n *\n * @author hubin\n * @since 2017-10-11\n */\npublic final class MybatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {\n\n    private static final Map<String, String> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>();\n    private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();\n    private final Class<E> enumClassType;\n    private final Class<?> propertyType;\n    private final Invoker getInvoker;\n\n    public MybatisEnumTypeHandler(Class<E> enumClassType) {\n        if (enumClassType == null) {\n            throw new IllegalArgumentException(\"Type argument cannot be null\");\n        }\n        this.enumClassType = enumClassType;\n        MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY);\n        String name = \"value\";\n        if (!IEnum.class.isAssignableFrom(enumClassType)) {\n            name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String.format(\"Could not find @EnumValue in Class: %s.\", this.enumClassType.getName())));\n        }\n        this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name));\n        this.getInvoker = metaClass.getGetInvoker(name);\n    }\n\n    /**\n     * 查找标记标记EnumValue字段\n     *\n     * @param clazz class\n     * @return EnumValue字段\n     * @since 3.3.1\n     */\n    public static Optional<String> findEnumValueFieldName(Class<?> clazz) {\n        if (clazz != null && clazz.isEnum()) {\n            String className = clazz.getName();\n            return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> {\n                Optional<Field> fieldOptional = findEnumValueAnnotationField(clazz);\n                return fieldOptional.map(Field::getName).orElse(null);\n            }));\n        }\n        return Optional.empty();\n    }\n\n    private static Optional<Field> findEnumValueAnnotationField(Class<?> clazz) {\n        return Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(EnumValue.class)).findFirst();\n    }\n\n    /**\n     * 判断是否为MP枚举处理\n     *\n     * @param clazz class\n     * @return 是否为MP枚举处理\n     * @since 3.3.1\n     */\n    public static boolean isMpEnums(Class<?> clazz) {\n        return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent());\n    }\n\n    @SuppressWarnings(\"Duplicates\")\n    @Override\n    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)\n        throws SQLException {\n        if (jdbcType == null) {\n            ps.setObject(i, this.getValue(parameter));\n        } else {\n            // see r3589\n            ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE);\n        }\n    }\n\n    @Override\n    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {\n        Object value = rs.getObject(columnName, this.propertyType);\n        if (null == value || rs.wasNull()) {\n            return null;\n        }\n        return this.valueOf(value);\n    }\n\n    @Override\n    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {\n        Object value = rs.getObject(columnIndex, this.propertyType);\n        if (null == value || rs.wasNull()) {\n            return null;\n        }\n        return this.valueOf(value);\n    }\n\n    @Override\n    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {\n        Object value = cs.getObject(columnIndex, this.propertyType);\n        if (null == value || cs.wasNull()) {\n            return null;\n        }\n        return this.valueOf(value);\n    }\n\n    private E valueOf(Object value) {\n        E[] es = this.enumClassType.getEnumConstants();\n        return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null);\n    }\n\n    /**\n     * 值比较\n     *\n     * @param sourceValue 数据库字段值\n     * @param targetValue 当前枚举属性值\n     * @return 是否匹配\n     * @since 3.3.0\n     */\n    protected boolean equalsValue(Object sourceValue, Object targetValue) {\n        String sValue = StringUtils.toStringTrim(sourceValue);\n        String tValue = StringUtils.toStringTrim(targetValue);\n        if (sourceValue instanceof Number && targetValue instanceof Number\n            && new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) {\n            return true;\n        }\n        return Objects.equals(sValue, tValue);\n    }\n\n    private Object getValue(Object object) {\n        try {\n            return this.getInvoker.invoke(object, new Object[0]);\n        } catch (ReflectiveOperationException e) {\n            throw ExceptionUtils.mpe(e);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/PostInitTableInfoHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.session.Configuration;\n\n/**\n * 初始化 TableInfo 同时进行一些操作\n *\n * @author miemie\n * @since 2022-09-20\n */\npublic interface PostInitTableInfoHandler {\n\n    /**\n     * 提供对 TableInfo 增强的能力\n     *\n     * @param configuration MybatisConfiguration\n     * @param entityType    实体类型\n     * @return {@link TableInfo}\n     */\n    default TableInfo creteTableInfo(Configuration configuration, Class<?> entityType) {\n        return new TableInfo(configuration, entityType);\n    }\n\n    /**\n     * 参与 TableInfo 初始化\n     *\n     * @param tableInfo     TableInfo\n     * @param configuration Configuration\n     */\n    default void postTableInfo(TableInfo tableInfo, Configuration configuration) {\n        // ignore\n    }\n\n    /**\n     * 参与 TableFieldInfo 初始化\n     *\n     * @param fieldInfo     TableFieldInfo\n     * @param configuration Configuration\n     */\n    default void postFieldInfo(TableFieldInfo fieldInfo, Configuration configuration) {\n        // ignore\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/StrictFill.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.handlers;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.util.function.Supplier;\n\n/**\n * 严格填充模式 model\n *\n * @author miemie\n * @since 2019-11-26\n */\n@Data\n@AllArgsConstructor\npublic class StrictFill<T, E extends T> {\n    /**\n     * 字段名\n     */\n    private String fieldName;\n    /**\n     * 字段类型\n     */\n    private Class<T> fieldType;\n    /**\n     * 获取字段值的函数\n     */\n    private Supplier<E> fieldVal;\n\n    public static <T, E extends T> StrictFill<T, E> of(String fieldName, Class<T> fieldType, E fieldVal) {\n        return new StrictFill<>(fieldName, fieldType, () -> fieldVal);\n    }\n\n    public static <T, E extends T> StrictFill<T, E> of(String fieldName, Supplier<E> fieldVal, Class<T> fieldType) {\n        return new StrictFill<>(fieldName, fieldType, fieldVal);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/handlers/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 处理器\n */\npackage com.baomidou.mybatisplus.core.handlers;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/DefaultIdentifierGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.incrementer;\n\nimport com.baomidou.mybatisplus.core.toolkit.Sequence;\nimport org.apache.ibatis.logging.Log;\n\nimport java.net.InetAddress;\n\n/**\n * 默认生成器\n *\n * @author nieqiuqiu\n * @since 2019-10-15\n * @since 3.3.0\n */\npublic class DefaultIdentifierGenerator implements IdentifierGenerator {\n\n    private final Sequence sequence;\n\n    /**\n     * @see #getInstance()\n     * @deprecated 3.5.3.2 共享默认单例\n     */\n    @Deprecated\n    public DefaultIdentifierGenerator() {\n        this.sequence = new Sequence(null);\n    }\n\n    public DefaultIdentifierGenerator(InetAddress inetAddress) {\n        this.sequence = new Sequence(inetAddress);\n    }\n\n    public DefaultIdentifierGenerator(long workerId, long dataCenterId) {\n        this.sequence = new Sequence(workerId, dataCenterId);\n    }\n\n    public DefaultIdentifierGenerator(Sequence sequence) {\n        this.sequence = sequence;\n    }\n\n    /**\n     * 获取固定的生成器实例\n     * <p>当无法通过网卡信息获取时，使用固定的一个主键生成器实例.</p>\n     *\n     * @param log 日志对象\n     * @return 主键生成器\n     * @since 3.5.13\n     */\n    public static IdentifierGenerator getFixedIdentifierGenerator(Log log) {\n        log.warn(\"Unable to obtain correct IP address information, the machine ID and serial number of the fixed machine will be used to generate the primary key.\");\n        return new DefaultIdentifierGenerator(1, 1);\n    }\n\n    @Override\n    public Long nextId(Object entity) {\n        return sequence.nextId();\n    }\n\n    public static DefaultIdentifierGenerator getInstance() {\n        return DefaultInstance.INSTANCE;\n    }\n\n    private static class DefaultInstance {\n\n        public static final DefaultIdentifierGenerator INSTANCE = new DefaultIdentifierGenerator();\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/IKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.incrementer;\n\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\n\n/**\n * 表主键生成器接口 (sql)\n *\n * @author hubin\n * @since 2017-05-08\n */\npublic interface IKeyGenerator {\n\n    /**\n     * 执行 key 生成 SQL\n     *\n     * @param incrementerName 序列名称(对应类上注解 {@link KeySequence#value()} 的值)\n     * @return sql\n     */\n    String executeSql(String incrementerName);\n\n    /**\n     * 数据库类型\n     */\n    DbType dbType();\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/IdentifierGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.incrementer;\n\n\nimport com.baomidou.mybatisplus.core.toolkit.IdWorker;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\n\n/**\n * Id生成器接口\n *\n * @author nieqiuqiu\n * @since 2019-10-15\n * @since 3.3.0\n */\npublic interface IdentifierGenerator {\n\n    /**\n     * 判断是否分配 ID\n     *\n     * @param idValue 主键值\n     * @return true 分配 false 无需分配\n     */\n    default boolean assignId(Object idValue) {\n        return StringUtils.checkValNull(idValue);\n    }\n\n    /**\n     * 生成Id\n     *\n     * @param entity 实体\n     * @return id\n     */\n    Number nextId(Object entity);\n\n    /**\n     * 生成uuid\n     *\n     * @param entity 实体\n     * @return uuid\n     */\n    default String nextUUID(Object entity) {\n        return IdWorker.get32UUID();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/ImadcnIdentifierGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.incrementer;\n\nimport com.imadcn.framework.idworker.config.ApplicationConfiguration;\nimport com.imadcn.framework.idworker.config.ZookeeperConfiguration;\nimport com.imadcn.framework.idworker.generator.CompressUUIDGenerator;\nimport com.imadcn.framework.idworker.generator.SnowflakeGenerator;\nimport com.imadcn.framework.idworker.register.zookeeper.ZookeeperWorkerRegister;\nimport com.imadcn.framework.idworker.registry.zookeeper.ZookeeperRegistryCenter;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\n/**\n * 用 <a href=\"https://github.com/imadcn/idworker\">idworker</a> 的实现\n *\n * @author miemie\n * @since 3.4.0\n * @since 2020-08-11\n */\npublic class ImadcnIdentifierGenerator implements IdentifierGenerator, Closeable {\n\n    private final SnowflakeGenerator idGenerator;\n    private final CompressUUIDGenerator uuidGenerator = new CompressUUIDGenerator();\n\n    public ImadcnIdentifierGenerator(String serverLists) {\n        this(configuration(serverLists));\n    }\n\n    public ImadcnIdentifierGenerator(ZookeeperConfiguration zookeeperConfiguration) {\n        this(zookeeperConfiguration, new ApplicationConfiguration());\n    }\n\n    public ImadcnIdentifierGenerator(ZookeeperConfiguration zookeeperConfiguration,\n                                     ApplicationConfiguration applicationConfiguration) {\n        ZookeeperRegistryCenter center = new ZookeeperRegistryCenter(zookeeperConfiguration);\n        ZookeeperWorkerRegister register = new ZookeeperWorkerRegister(center, applicationConfiguration);\n        idGenerator = new SnowflakeGenerator(register);\n        idGenerator.init();\n    }\n\n    private static ZookeeperConfiguration configuration(String serverLists) {\n        ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration();\n        zookeeperConfiguration.setServerLists(serverLists);\n        return zookeeperConfiguration;\n    }\n\n    @Override\n    public Number nextId(Object entity) {\n        return idGenerator.nextId();\n    }\n\n    @Override\n    public String nextUUID(Object entity) {\n        return uuidGenerator.nextStringId();\n    }\n\n    @Override\n    public void close() throws IOException {\n        idGenerator.close();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/incrementer/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * key 生成器\n */\npackage com.baomidou.mybatisplus.core.incrementer;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/AbstractMethod.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.builder.SqlSourceBuilder;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.mapping.SqlSource;\nimport org.apache.ibatis.mapping.StatementType;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * 抽象的注入方法类\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic abstract class AbstractMethod implements Constants {\n\n    protected final Log logger = LogFactory.getLog(getClass());\n\n    protected Configuration configuration;\n\n    protected LanguageDriver languageDriver;\n\n    protected MapperBuilderAssistant builderAssistant;\n\n    /**\n     * 方法名称\n     *\n     * @since 3.5.0\n     */\n    protected final String methodName;\n\n    /**\n     * @param methodName 方法名\n     * @since 3.5.0\n     */\n    protected AbstractMethod(String methodName) {\n        Assert.notNull(methodName, \"方法名不能为空\");\n        this.methodName = methodName;\n    }\n\n    /**\n     * 注入自定义方法\n     */\n    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        this.configuration = builderAssistant.getConfiguration();\n        this.builderAssistant = builderAssistant;\n        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();\n        /* 注入自定义方法 */\n        injectMappedStatement(mapperClass, modelClass, tableInfo);\n    }\n\n    /**\n     * 是否已经存在MappedStatement\n     *\n     * @param mappedStatement MappedStatement\n     * @return true or false\n     */\n    private boolean hasMappedStatement(String mappedStatement) {\n        return configuration.hasStatement(mappedStatement, false);\n    }\n\n    /**\n     * SQL 更新 set 语句\n     *\n     * @param table 表信息\n     * @return sql set 片段\n     */\n    protected String sqlLogicSet(TableInfo table) {\n        return \"SET \" + table.getLogicDeleteSql(false, false);\n    }\n\n    /**\n     * SQL 更新 set 语句\n     *\n     * @param logic  是否逻辑删除注入器\n     * @param ew     是否存在 UpdateWrapper 条件\n     * @param table  表信息\n     * @param alias  别名\n     * @param prefix 前缀\n     * @return sql\n     */\n    protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias,\n                            final String prefix) {\n        String sqlScript = table.getAllSqlSet(logic, prefix);\n        if (judgeAliasNull) {\n            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format(\"%s != null\", alias), true);\n        }\n        if (ew) {\n            sqlScript = sqlScript + NEWLINE + convertIfEwParam(U_WRAPPER_SQL_SET, false);\n        }\n        return SqlScriptUtils.convertSet(sqlScript);\n    }\n\n    /**\n     * SQL 注释\n     *\n     * @return sql\n     */\n    protected String sqlComment() {\n        return NEWLINE + convertIfEwParam(Q_WRAPPER_SQL_COMMENT, true);\n    }\n\n    /**\n     * SQL 注释\n     *\n     * @return sql\n     */\n    protected String sqlFirst() {\n        return convertIfEwParam(Q_WRAPPER_SQL_FIRST, true);\n    }\n\n    protected String convertIfEwParam(final String param, final boolean newLine) {\n        return SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(param),\n            String.format(\"%s != null and %s != null\", WRAPPER, param), newLine);\n    }\n\n    /**\n     * SQL 查询所有表字段\n     *\n     * @param table        表信息\n     * @param queryWrapper 是否为使用 queryWrapper 查询\n     * @return sql 脚本\n     */\n    protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) {\n        /* 假设存在用户自定义的 resultMap 映射返回 */\n        String selectColumns = ASTERISK;\n        if (table.getResultMap() == null || table.isAutoInitResultMap()) {\n            /* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */\n            selectColumns = table.getAllSqlSelect();\n        }\n        if (!queryWrapper) {\n            return selectColumns;\n        }\n        return convertChooseEwSelect(selectColumns);\n    }\n\n    /**\n     * SQL 查询记录行数\n     *\n     * @return count sql 脚本\n     */\n    protected String sqlCount() {\n        return convertChooseEwSelect(ASTERISK);\n    }\n\n    /**\n     * SQL 设置selectObj sql select\n     *\n     * @param table 表信息\n     */\n    protected String sqlSelectObjsColumns(TableInfo table) {\n        return convertChooseEwSelect(table.getAllSqlSelect());\n    }\n\n    protected String convertChooseEwSelect(final String otherwise) {\n        return SqlScriptUtils.convertChoose(String.format(\"%s != null and %s != null\", WRAPPER, Q_WRAPPER_SQL_SELECT),\n            SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT), otherwise);\n    }\n\n    /**\n     * SQL map 查询条件\n     */\n    @Deprecated\n    protected String sqlWhereByMap(TableInfo table) {\n        if (table.isWithLogicDelete()) {\n            // 逻辑删除\n            String sqlScript = SqlScriptUtils.convertChoose(\"v == null\", \" ${k} IS NULL \",\n                \" ${k} = #{v} \");\n            sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, \"k\", \"v\", \"AND\");\n            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format(\"%s != null and !%s.isEmpty\", COLUMN_MAP, COLUMN_MAP), true);\n            sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true));\n            return SqlScriptUtils.convertWhere(sqlScript);\n        } else {\n            String sqlScript = SqlScriptUtils.convertChoose(\"v == null\", \" ${k} IS NULL \",\n                \" ${k} = #{v} \");\n            sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, \"k\", \"v\", \"AND\");\n            sqlScript = SqlScriptUtils.convertWhere(sqlScript);\n            return SqlScriptUtils.convertIf(sqlScript, String.format(\"%s != null and !%s\", COLUMN_MAP,\n                COLUMN_MAP_IS_EMPTY), true);\n        }\n    }\n\n    private static final String BIND_SQL_SEGMENT = \"<bind name=\\\"_sgEs_\\\" value=\\\"ew.sqlSegment != null and ew.sqlSegment != ''\\\"/>\";\n\n    private static final String AND_SQL_SEGMENT = SqlScriptUtils.convertIf(\" AND ${\" + WRAPPER_SQLSEGMENT + \"}\", \"_sgEs_ and \" + WRAPPER_NONEMPTYOFNORMAL, true);\n\n    private static final String LAST_SQL_SEGMENT = SqlScriptUtils.convertIf(\" ${\" + WRAPPER_SQLSEGMENT + \"}\", \"_sgEs_ and \" + WRAPPER_EMPTYOFNORMAL, true);\n\n    /**\n     * EntityWrapper方式获取select where\n     *\n     * @param newLine 是否提到下一行\n     * @param table   表信息\n     * @return String\n     */\n    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {\n        /*\n         * 存在逻辑删除 SQL 注入\n         */\n        if (table.isWithLogicDelete()) {\n            String sqlScript = table.getAllSqlWhere(true, true, true, WRAPPER_ENTITY_DOT);\n            sqlScript = SqlScriptUtils.convertIf(sqlScript, WRAPPER_ENTITY + \" != null\", true);\n            sqlScript = SqlScriptUtils.convertIf(BIND_SQL_SEGMENT + NEWLINE + sqlScript + NEWLINE + AND_SQL_SEGMENT + NEWLINE + LAST_SQL_SEGMENT,\n                WRAPPER + \" != null\", true);\n            sqlScript = SqlScriptUtils.convertWhere(table.getLogicDeleteSql(false, true) + NEWLINE + sqlScript);\n            return newLine ? NEWLINE + sqlScript : sqlScript;\n        }\n        /*\n         * 普通 SQL 注入\n         */\n        String sqlScript = table.getAllSqlWhere(false, false, true, WRAPPER_ENTITY_DOT);\n        sqlScript = SqlScriptUtils.convertIf(sqlScript, WRAPPER_ENTITY + \" != null\", true);\n        sqlScript = SqlScriptUtils.convertWhere(sqlScript + NEWLINE + AND_SQL_SEGMENT) + NEWLINE + LAST_SQL_SEGMENT;\n        sqlScript = SqlScriptUtils.convertIf(BIND_SQL_SEGMENT + NEWLINE + sqlScript, WRAPPER + \" != null\", true);\n        return newLine ? NEWLINE + sqlScript : sqlScript;\n    }\n\n    protected String sqlOrderBy(TableInfo tableInfo) {\n        /* 不存在排序字段，直接返回空 */\n        List<OrderFieldInfo> orderByFields = tableInfo.getOrderByFields();\n        if (CollectionUtils.isEmpty(orderByFields)) {\n            return StringPool.EMPTY;\n        }\n        orderByFields.sort(Comparator.comparingInt(OrderFieldInfo::getSort));\n        StringBuilder sql = new StringBuilder();\n        sql.append(NEWLINE).append(\" ORDER BY \");\n        sql.append(orderByFields.stream().map(orderFieldInfo -> String.format(\"%s %s\", orderFieldInfo.getColumn(),\n            orderFieldInfo.getType())).collect(joining(\",\")));\n        /* 当wrapper中传递了orderBy属性，@orderBy注解失效 */\n        return SqlScriptUtils.convertIf(sql.toString(), String.format(\"%s == null or %s\", WRAPPER,\n            WRAPPER_EXPRESSION_ORDER), true);\n    }\n\n    /**\n     * 过滤 TableFieldInfo 集合, join 成字符串\n     */\n    protected String filterTableFieldInfo(List<TableFieldInfo> fieldList, Predicate<TableFieldInfo> predicate,\n                                          Function<TableFieldInfo, String> function, String joiningVal) {\n        Stream<TableFieldInfo> infoStream = fieldList.stream();\n        if (predicate != null) {\n            return infoStream.filter(predicate).map(function).collect(joining(joiningVal));\n        }\n        return infoStream.map(function).collect(joining(joiningVal));\n    }\n\n    /**\n     * 获取乐观锁相关\n     *\n     * @param tableInfo 表信息\n     * @return String\n     */\n    protected String optlockVersion(TableInfo tableInfo) {\n        if (tableInfo.isWithVersion()) {\n            return tableInfo.getVersionFieldInfo().getVersionOli(ENTITY, ENTITY_DOT);\n        }\n        return EMPTY;\n    }\n\n    /**\n     * 查询\n     */\n    protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, String id, SqlSource sqlSource,\n                                                               TableInfo table) {\n        String resultMap = table.getResultMap();\n        if (null != resultMap) {\n            /* 返回 resultMap 映射结果集 */\n            return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,\n                resultMap, null, NoKeyGenerator.INSTANCE, null, null);\n        } else {\n            /* 普通查询 */\n            return addSelectMappedStatementForOther(mapperClass, id, sqlSource, table.getEntityType());\n        }\n    }\n\n    /**\n     * 查询\n     *\n     * @since 3.5.0\n     */\n    protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, SqlSource sqlSource, TableInfo table) {\n        return addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, table);\n    }\n\n    /**\n     * 查询\n     */\n    protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, String id, SqlSource sqlSource,\n                                                               Class<?> resultType) {\n        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,\n            null, resultType, NoKeyGenerator.INSTANCE, null, null);\n    }\n\n    /**\n     * 查询\n     *\n     * @since 3.5.0\n     */\n    protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, SqlSource sqlSource, Class<?> resultType) {\n        return addSelectMappedStatementForOther(mapperClass, this.methodName, sqlSource, resultType);\n    }\n\n    /**\n     * 插入\n     */\n    protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,\n                                                       SqlSource sqlSource, KeyGenerator keyGenerator,\n                                                       String keyProperty, String keyColumn) {\n        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null,\n            Integer.class, keyGenerator, keyProperty, keyColumn);\n    }\n\n    /**\n     * 插入\n     *\n     * @since 3.5.0\n     */\n    protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType,\n                                                       SqlSource sqlSource, KeyGenerator keyGenerator,\n                                                       String keyProperty, String keyColumn) {\n        return addInsertMappedStatement(mapperClass, parameterType, this.methodName, sqlSource, keyGenerator, keyProperty, keyColumn);\n    }\n\n\n    /**\n     * 删除\n     */\n    protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {\n        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null,\n            null, Integer.class, NoKeyGenerator.INSTANCE, null, null);\n    }\n\n    /**\n     * @since 3.5.0\n     */\n    protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, SqlSource sqlSource) {\n        return addDeleteMappedStatement(mapperClass, this.methodName, sqlSource);\n    }\n\n    /**\n     * 更新\n     */\n    protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,\n                                                       SqlSource sqlSource) {\n        return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, parameterType, null,\n            Integer.class, NoKeyGenerator.INSTANCE, null, null);\n    }\n\n    /**\n     * 更新\n     *\n     * @since 3.5.0\n     */\n    protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType,\n                                                       SqlSource sqlSource) {\n        return addUpdateMappedStatement(mapperClass, parameterType, this.methodName, sqlSource);\n    }\n\n    /**\n     * 添加 MappedStatement 到 Mybatis 容器\n     */\n    protected MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,\n                                                 SqlCommandType sqlCommandType, Class<?> parameterType,\n                                                 String resultMap, Class<?> resultType, KeyGenerator keyGenerator,\n                                                 String keyProperty, String keyColumn) {\n        String statementName = mapperClass.getName() + DOT + id;\n        if (hasMappedStatement(statementName)) {\n            logger.warn(LEFT_SQ_BRACKET + statementName + \"] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [\" + getClass() + RIGHT_SQ_BRACKET);\n            return null;\n        }\n        /* 缓存逻辑处理 */\n        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;\n        return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType,\n            null, null, null, parameterType, resultMap, resultType,\n            null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,\n            configuration.getDatabaseId(), languageDriver, null);\n    }\n\n    /**\n     * @since 3.5.0\n     */\n    protected MappedStatement addMappedStatement(Class<?> mapperClass, SqlSource sqlSource,\n                                                 SqlCommandType sqlCommandType, Class<?> parameterType,\n                                                 String resultMap, Class<?> resultType, KeyGenerator keyGenerator,\n                                                 String keyProperty, String keyColumn) {\n        return addMappedStatement(mapperClass, this.methodName, sqlSource, sqlCommandType, parameterType, resultMap, resultType, keyGenerator, keyProperty, keyColumn);\n    }\n\n    /**\n     * 注入自定义 MappedStatement\n     *\n     * @param mapperClass mapper 接口\n     * @param modelClass  mapper 泛型\n     * @param tableInfo   数据库表反射信息\n     * @return MappedStatement\n     */\n    public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);\n\n\n    /**\n     * @param configuration 配置对象\n     * @param script        (统一去除空白行)\n     * @param parameterType 参数类型\n     * @return SqlSource\n     * @since 3.5.3.2\n     */\n    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {\n        return languageDriver.createSqlSource(configuration, SqlSourceBuilder.removeExtraWhitespaces(script), parameterType);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/AbstractSqlInjector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector;\n\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * SQL 自动注入器\n *\n * @author hubin\n * @since 2018-04-07\n */\npublic abstract class AbstractSqlInjector implements ISqlInjector {\n\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n    @Override\n    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {\n        Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);\n        if (modelClass != null) {\n            String className = mapperClass.toString();\n            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());\n            if (!mapperRegistryCache.contains(className)) {\n                TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);\n                List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);\n                // 兼容旧代码\n                if (CollectionUtils.isEmpty(methodList)) {\n                    methodList = this.getMethodList(builderAssistant.getConfiguration(), mapperClass, tableInfo);\n                }\n                if (CollectionUtils.isNotEmpty(methodList)) {\n                    // 循环注入自定义方法\n                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));\n                } else {\n                    logger.debug(className + \", No effective injection method was found.\");\n                }\n                mapperRegistryCache.add(className);\n            }\n        }\n    }\n\n    /**\n     * <p>\n     * 获取 注入的方法\n     * </p>\n     *\n     * @param mapperClass 当前mapper\n     * @param tableInfo   表信息\n     * @return 注入的方法集合\n     * @since 3.1.2 add  mapperClass\n     * @deprecated 3.5.6 {@link #getMethodList(Configuration, Class, TableInfo)}\n     */\n    @Deprecated\n    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {\n        return getMethodList(tableInfo.getConfiguration(), mapperClass, tableInfo);\n    }\n\n    /**\n     * 获取注入的方法\n     *\n     * @param configuration 配置对象\n     * @param mapperClass   当前mapper\n     * @param tableInfo     表信息\n     * @return 注入方法集合\n     * @since 3.5.6\n     */\n    public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n        return new ArrayList<>();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/DefaultSqlInjector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.injector.methods.*;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.toList;\n\n\n/**\n * SQL 默认注入器\n *\n * @author hubin\n * @since 2018-04-10\n */\npublic class DefaultSqlInjector extends AbstractSqlInjector {\n\n    @Override\n    public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {\n        GlobalConfig.DbConfig dbConfig = GlobalConfigUtils.getDbConfig(configuration);\n        Stream.Builder<AbstractMethod> builder = Stream.<AbstractMethod>builder()\n            .add(new Insert(dbConfig.isInsertIgnoreAutoIncrementColumn()))\n            .add(new Delete())\n            .add(new Update())\n            .add(new SelectCount())\n            .add(new SelectMaps())\n            .add(new SelectObjs())\n            .add(new SelectList());\n        if (tableInfo.havePK()) {\n            builder.add(new DeleteById())\n                .add(new DeleteByIds())\n                .add(new UpdateById())\n                .add(new SelectById())\n                .add(new SelectByIds());\n        } else {\n            logger.warn(String.format(\"%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.\",\n                tableInfo.getEntityType()));\n        }\n        return builder.build().collect(toList());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/ISqlInjector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector;\n\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\n\n/**\n * SQL 自动注入器接口\n *\n * @author hubin\n * @since 2016-07-24\n */\npublic interface ISqlInjector {\n\n    /**\n     * 检查SQL是否注入(已经注入过不再注入)\n     *\n     * @param builderAssistant mapper 信息\n     * @param mapperClass      mapper 接口的 class 对象\n     */\n    void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/SqlRunnerInjector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector;\n\nimport com.baomidou.mybatisplus.core.assist.ISqlRunner;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.mapping.SqlSource;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.ArrayList;\nimport java.util.Map;\n\n/**\n * SqlRunner 注入器\n *\n * @author hubin\n * @since 2018-04-08\n */\npublic class SqlRunnerInjector {\n\n    private static final Log logger = LogFactory.getLog(SqlRunnerInjector.class);\n\n    protected Configuration configuration;\n    protected LanguageDriver languageDriver;\n\n    /**\n     * 注入动态执行脚本\n     *\n     * @since 3.5.12\n     */\n    public static final String SQL_SCRIPT = \"<script>\" +\n        \"${sql}\\n\" +\n        \"<if test=\\\"true\\\"></if>\" +\n        \"</script>\";\n\n\n    public void inject(Configuration configuration) {\n        this.configuration = configuration;\n        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();\n        this.initSelectList();\n        this.initSelectObjs();\n        this.initInsert();\n        this.initUpdate();\n        this.initDelete();\n        this.initCount();\n    }\n\n    /**\n     * 是否已经存在MappedStatement\n     *\n     * @param mappedStatement mappedStatement\n     * @return 是否存在\n     */\n    private boolean hasMappedStatement(String mappedStatement) {\n        return configuration.hasStatement(mappedStatement, false);\n    }\n\n    /**\n     * 创建查询MappedStatement\n     *\n     * @param mappedStatement mappedStatement\n     * @param sqlSource       执行的sqlSource\n     * @param resultType      返回的结果类型\n     */\n    private void createSelectMappedStatement(String mappedStatement, SqlSource sqlSource, final Class<?> resultType) {\n        MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, SqlCommandType.SELECT)\n            .resultMaps(new ArrayList<ResultMap>() {\n                {\n                    add(new ResultMap.Builder(configuration, \"defaultResultMap\", resultType, new ArrayList<>(0))\n                        .build());\n                }\n            }).build();\n        // 缓存\n        configuration.addMappedStatement(ms);\n    }\n\n    /**\n     * 创建一个MappedStatement\n     *\n     * @param mappedStatement mappedStatement\n     * @param sqlSource       执行的sqlSource\n     * @param sqlCommandType  执行的sqlCommandType\n     */\n    private void createUpdateMappedStatement(String mappedStatement, SqlSource sqlSource, SqlCommandType sqlCommandType) {\n        MappedStatement ms = new MappedStatement.Builder(configuration, mappedStatement, sqlSource, sqlCommandType).resultMaps(\n            new ArrayList<ResultMap>() {\n                {\n                    add(new ResultMap.Builder(configuration, \"defaultResultMap\", int.class, new ArrayList<>(0))\n                        .build());\n                }\n            }).build();\n        // 缓存\n        configuration.addMappedStatement(ms);\n    }\n\n    /**\n     * initSelectList\n     */\n    private void initSelectList() {\n        if (hasMappedStatement(ISqlRunner.SELECT_LIST)) {\n            logger.warn(\"MappedStatement 'SqlRunner.SelectList' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class);\n        createSelectMappedStatement(ISqlRunner.SELECT_LIST, sqlSource, Map.class);\n    }\n\n    /**\n     * initSelectObjs\n     */\n    private void initSelectObjs() {\n        if (hasMappedStatement(ISqlRunner.SELECT_OBJS)) {\n            logger.warn(\"MappedStatement 'SqlRunner.SelectObjs' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Object.class);\n        createSelectMappedStatement(ISqlRunner.SELECT_OBJS, sqlSource, Object.class);\n    }\n\n    /**\n     * initCount\n     */\n    private void initCount() {\n        if (hasMappedStatement(ISqlRunner.COUNT)) {\n            logger.warn(\"MappedStatement 'SqlRunner.Count' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class);\n        createSelectMappedStatement(ISqlRunner.COUNT, sqlSource, Long.class);\n    }\n\n    /**\n     * initInsert\n     */\n    private void initInsert() {\n        if (hasMappedStatement(ISqlRunner.INSERT)) {\n            logger.warn(\"MappedStatement 'SqlRunner.Insert' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class);\n        createUpdateMappedStatement(ISqlRunner.INSERT, sqlSource, SqlCommandType.INSERT);\n    }\n\n    /**\n     * initUpdate\n     */\n    private void initUpdate() {\n        if (hasMappedStatement(ISqlRunner.UPDATE)) {\n            logger.warn(\"MappedStatement 'SqlRunner.Update' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class);\n        createUpdateMappedStatement(ISqlRunner.UPDATE, sqlSource, SqlCommandType.UPDATE);\n    }\n\n    /**\n     * initDelete\n     */\n    private void initDelete() {\n        if (hasMappedStatement(ISqlRunner.DELETE)) {\n            logger.warn(\"MappedStatement 'SqlRunner.Delete' Already Exists\");\n            return;\n        }\n        SqlSource sqlSource = languageDriver.createSqlSource(configuration, SQL_SCRIPT, Map.class);\n        createUpdateMappedStatement(ISqlRunner.DELETE, sqlSource, SqlCommandType.DELETE);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Delete.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 根据 entity 条件删除记录\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class Delete extends AbstractMethod {\n\n    public Delete() {\n        this(SqlMethod.DELETE.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public Delete(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        if (tableInfo.isWithLogicDelete()) {\n            /*\n             * 逻辑删除\n             */\n            String sql = SqlMethod.LOGIC_DELETE.format(tableInfo.getTableName(), sqlLogicSet(tableInfo),\n                sqlWhereEntityWrapper(true, tableInfo),\n                sqlComment());\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n            return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n        }\n\n        // 真实删除\n        String sql = SqlMethod.DELETE.format(tableInfo.getTableName(),\n            sqlWhereEntityWrapper(true, tableInfo),\n            sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteBatchByIds.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\n/**\n * 根据 ID 集合删除\n *\n * @author hubin\n * @since 2018-04-06\n * @deprecated 3.5.7 {@link DeleteByIds}\n */\n@Deprecated\npublic class DeleteBatchByIds extends DeleteByIds {\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteById.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.List;\n\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toList;\n\n/**\n * 根据 ID 删除\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class DeleteById extends AbstractMethod {\n\n    public DeleteById() {\n        this(SqlMethod.DELETE_BY_ID.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public DeleteById(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql;\n        if (tableInfo.isWithLogicDelete()) {\n            List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()\n                .filter(TableFieldInfo::isWithUpdateFill)\n                .filter(f -> !f.isLogicDelete())\n                .collect(toList());\n            if (CollectionUtils.isNotEmpty(fieldInfos)) {\n                String sqlSet = \"SET \" + SqlScriptUtils.convertIf(fieldInfos.stream()\n                    .map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY)),\n                    \"@org.apache.ibatis.reflection.SystemMetaObject@forObject(_parameter).findProperty('\" + tableInfo.getKeyProperty() + \"', false) != null\", true)\n                    + tableInfo.getLogicDeleteSql(false, false);\n                sql = SqlMethod.LOGIC_DELETE_BY_ID.format(tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),\n                    tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true));\n            } else {\n                sql = SqlMethod.LOGIC_DELETE_BY_ID.format(tableInfo.getTableName(), sqlLogicSet(tableInfo),\n                    tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),\n                    tableInfo.getLogicDeleteSql(true, true));\n            }\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class);\n            return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n        } else {\n            sql = SqlMethod.DELETE_BY_ID.format(tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty());\n            return this.addDeleteMappedStatement(mapperClass, methodName, super.createSqlSource(configuration, sql, Object.class));\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteByIds.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.List;\n\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toList;\n\n\n/**\n * 根据 ID 集合删除\n *\n * @author nieqiurong\n * @since 3.5.7\n */\npublic class DeleteByIds extends AbstractMethod {\n\n    public DeleteByIds() {\n        this(SqlMethod.DELETE_BY_IDS.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public DeleteByIds(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql;\n        if (tableInfo.isWithLogicDelete()) {\n            sql = logicDeleteScript(tableInfo, SqlMethod.LOGIC_DELETE_BY_IDS);\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class);\n            return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n        } else {\n            sql = SqlMethod.DELETE_BY_IDS.format(tableInfo.getTableName(), tableInfo.getKeyColumn(), getConvertForeachScript(tableInfo));\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, Object.class);\n            return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource);\n        }\n    }\n\n    protected String getConvertForeachScript(TableInfo tableInfo) {\n        return SqlScriptUtils.convertForeach(\n            SqlScriptUtils.convertChoose(\"item!=null and @org.apache.ibatis.reflection.SystemMetaObject@forObject(item).findProperty('\" + tableInfo.getKeyProperty() + \"', false) != null\",\n                \"#{item.\" + tableInfo.getKeyProperty() + \"}\", \"#{item}\"),\n            COLL, null, \"item\", COMMA);\n    }\n\n    /**\n     * @param tableInfo 表信息\n     * @return 逻辑删除脚本\n     * @since 3.5.0\n     */\n    public String logicDeleteScript(TableInfo tableInfo, SqlMethod sqlMethod) {\n        List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()\n            .filter(TableFieldInfo::isWithUpdateFill)\n            .filter(f -> !f.isLogicDelete())\n            .collect(toList());\n        String sqlSet = \"SET \";\n        if (CollectionUtils.isNotEmpty(fieldInfos)) {\n            sqlSet += SqlScriptUtils.convertIf(fieldInfos.stream()\n                .map(i -> i.getSqlSet(Constants.MP_FILL_ET + StringPool.DOT)).collect(joining(EMPTY)), String.format(\"%s != null\", Constants.MP_FILL_ET), true);\n        }\n        sqlSet += StringPool.EMPTY + tableInfo.getLogicDeleteSql(false, false);\n        return sqlMethod.format(tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),\n            getConvertForeachScript(tableInfo), tableInfo.getLogicDeleteSql(true, true));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/DeleteByMap.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.Map;\n\n/**\n * 根据columnMap 条件删除记录\n *\n * @author hubin\n * @since 2018-04-06\n */\n@Deprecated\npublic class DeleteByMap extends AbstractMethod {\n\n    public DeleteByMap() {\n        this(SqlMethod.DELETE_BY_MAP.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public DeleteByMap(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql;\n        if (tableInfo.isWithLogicDelete()) {\n            sql = SqlMethod.LOGIC_DELETE_BY_MAP.format(tableInfo.getTableName(), sqlLogicSet(tableInfo), sqlWhereByMap(tableInfo));\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class);\n            return addUpdateMappedStatement(mapperClass, Map.class, methodName, sqlSource);\n        } else {\n            sql = SqlMethod.DELETE_BY_MAP.format(tableInfo.getTableName(), this.sqlWhereByMap(tableInfo));\n            SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class);\n            return this.addDeleteMappedStatement(mapperClass, methodName, sqlSource);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Insert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 插入一条数据（选择字段插入）\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class Insert extends AbstractMethod {\n\n    /**\n     * 自增主键字段是否忽略\n     *\n     * @since 3.5.4\n     */\n    private boolean ignoreAutoIncrementColumn;\n\n    public Insert() {\n        this(SqlMethod.INSERT_ONE.getMethod());\n    }\n\n    /**\n     * @param ignoreAutoIncrementColumn 是否忽略自增长主键字段\n     * @since 3.5.4\n     */\n    public Insert(boolean ignoreAutoIncrementColumn) {\n        this(SqlMethod.INSERT_ONE.getMethod());\n        this.ignoreAutoIncrementColumn = ignoreAutoIncrementColumn;\n    }\n\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public Insert(String name) {\n        super(name);\n    }\n\n    /**\n     * @param name                      方法名\n     * @param ignoreAutoIncrementColumn 是否忽略自增长主键字段\n     * @since 3.5.4\n     */\n    public Insert(String name, boolean ignoreAutoIncrementColumn) {\n        super(name);\n        this.ignoreAutoIncrementColumn = ignoreAutoIncrementColumn;\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;\n        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null, ignoreAutoIncrementColumn),\n            LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);\n        String valuesScript = LEFT_BRACKET + NEWLINE + SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null, ignoreAutoIncrementColumn),\n            null, null, null, COMMA) + NEWLINE + RIGHT_BRACKET;\n        String keyProperty = null;\n        String keyColumn = null;\n        // 表包含主键处理逻辑,如果不包含主键当普通字段处理\n        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {\n            if (tableInfo.getIdType() == IdType.AUTO) {\n                /* 自增主键 */\n                keyGenerator = Jdbc3KeyGenerator.INSTANCE;\n                keyProperty = tableInfo.getKeyProperty();\n                // 去除转义符\n                keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());\n            } else if (null != tableInfo.getKeySequence()) {\n                keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);\n                keyProperty = tableInfo.getKeyProperty();\n                keyColumn = tableInfo.getKeyColumn();\n            }\n        }\n        String sql = SqlMethod.INSERT_ONE.format(tableInfo.getTableName(), columnScript, valuesScript);\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectBatchByIds.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\n/**\n * 根据ID集合，批量查询数据\n *\n * @author hubin\n * @since 2018-04-06\n * @deprecated 3.5.8 {@link SelectByIds}\n */\n@Deprecated\npublic class SelectBatchByIds extends SelectByIds {\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectById.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 根据ID 查询一条数据\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class SelectById extends AbstractMethod {\n\n    public SelectById() {\n        this(SqlMethod.SELECT_BY_ID.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectById(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_BY_ID.format(\n            sqlSelectColumns(tableInfo, false),\n            tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),\n            tableInfo.getLogicDeleteSql(true, true)), Object.class);\n        return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectByIds.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 根据ID集合，批量查询数据\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class SelectByIds extends AbstractMethod {\n\n    public SelectByIds() {\n        this(SqlMethod.SELECT_BY_IDS.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectByIds(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_BY_IDS.format(\n            sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(),\n            SqlScriptUtils.convertForeach(\"#{item}\", COLL, null, \"item\", COMMA),\n            tableInfo.getLogicDeleteSql(true, true)), Object.class);\n        return addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectByMap.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.Map;\n\n/**\n * 根据columnMap 查询一条数据\n *\n * @author hubin\n * @since 2018-04-06\n */\n@Deprecated\npublic class SelectByMap extends AbstractMethod {\n\n    public SelectByMap() {\n        this(SqlMethod.SELECT_BY_MAP.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectByMap(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_BY_MAP.format(sqlSelectColumns(tableInfo, false),\n            tableInfo.getTableName(), sqlWhereByMap(tableInfo));\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, Map.class);\n        return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectCount.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 查询满足条件总记录数\n *\n * @author hubin\n * @since 2018-04-08\n */\npublic class SelectCount extends AbstractMethod {\n\n    public SelectCount() {\n        this(SqlMethod.SELECT_COUNT.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectCount(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_COUNT.format(sqlFirst(), sqlCount(), tableInfo.getTableName(),\n            sqlWhereEntityWrapper(true, tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Long.class);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectList.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport org.apache.ibatis.builder.annotation.ProviderContext;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 查询满足条件所有数据\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class SelectList extends AbstractMethod {\n\n    public SelectList() {\n        this(SqlMethod.SELECT_LIST.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectList(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        SqlSource sqlSource = super.createSqlSource(configuration, getSql(tableInfo), modelClass);\n        return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n\n    protected String getSql(TableInfo tableInfo) {\n        return SqlMethod.SELECT_LIST.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),\n            sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment());\n    }\n\n    /**\n     * 游标查询方法\n     */\n    public String selectWithCursor(ProviderContext context) {\n        return TableInfoHelper.getSqlScript(context, this::getSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectMaps.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.Map;\n\n/**\n * 查询满足条件所有数据\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class SelectMaps extends AbstractMethod {\n\n    public SelectMaps() {\n        this(SqlMethod.SELECT_MAPS.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectMaps(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_MAPS.format(sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),\n            sqlWhereEntityWrapper(true, tableInfo),sqlOrderBy(tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Map.class);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectMapsPage.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.Map;\n\n/**\n * 查询满足条件所有数据（并翻页）\n *\n * @author hubin\n * @deprecated 3.5.3.2 {@link  com.baomidou.mybatisplus.core.mapper.BaseMapper#selectMaps(com.baomidou.mybatisplus.core.metadata.IPage, com.baomidou.mybatisplus.core.conditions.Wrapper)}\n * @since 2018-04-06\n */\n@Deprecated\npublic class SelectMapsPage extends AbstractMethod {\n\n    public SelectMapsPage() {\n        this(SqlMethod.SELECT_MAPS_PAGE.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectMapsPage(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_MAPS_PAGE.format(sqlFirst(), sqlSelectColumns(tableInfo, true),\n            tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Map.class);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectObjs.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 查询满足条件所有数据\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class SelectObjs extends AbstractMethod {\n\n    public SelectObjs() {\n        this(SqlMethod.SELECT_OBJS.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectObjs(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_OBJS.format(sqlFirst(), sqlSelectObjsColumns(tableInfo),\n            tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo),sqlOrderBy(tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addSelectMappedStatementForOther(mapperClass, methodName, sqlSource, Object.class);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectOne.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 查询满足条件一条数据，为了精简注入方法，该方法采用 list.get(0) 处理后续不再使用\n *\n * @author hubin\n * @since 2018-04-06\n */\n@Deprecated\npublic class SelectOne extends AbstractMethod {\n\n    public SelectOne() {\n        this(SqlMethod.SELECT_ONE.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public SelectOne(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        SqlSource sqlSource = super.createSqlSource(configuration, SqlMethod.SELECT_ONE.format(sqlFirst(),\n            sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),\n            sqlWhereEntityWrapper(true, tableInfo), sqlComment()), modelClass);\n        return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/SelectPage.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 查询满足条件所有数据（并翻页）\n *\n * @author hubin\n * @since 2018-04-06\n * @deprecated 3.5.3.2 {@link BaseMapper#selectList(IPage, Wrapper)}\n */\n@Deprecated\npublic class SelectPage extends AbstractMethod {\n\n    public SelectPage() {\n        this(SqlMethod.SELECT_PAGE.getMethod());\n    }\n\n    /**\n     * @since 3.5.0\n     * @param name 方法名\n     */\n    public SelectPage(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.SELECT_PAGE.format(sqlFirst(), sqlSelectColumns(tableInfo, true),\n            tableInfo.getTableName(), sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/Update.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 根据 whereEntity 条件，更新记录\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class Update extends AbstractMethod {\n\n    public Update() {\n        this(SqlMethod.UPDATE.getMethod());\n    }\n\n    /**\n     * @since 3.5.0\n     * @param name 方法名\n     */\n    public Update(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql = SqlMethod.UPDATE.format(tableInfo.getTableName(),\n            sqlSet(true, true, tableInfo, true, ENTITY, ENTITY_DOT),\n            sqlWhereEntityWrapper(true, tableInfo), sqlComment());\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/UpdateById.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 根据 ID 更新有值字段\n *\n * @author hubin\n * @since 2018-04-06\n */\npublic class UpdateById extends AbstractMethod {\n\n    public UpdateById() {\n        this(SqlMethod.UPDATE_BY_ID.getMethod());\n    }\n\n    /**\n     * @since 3.5.0\n     * @param name 方法名\n     */\n    public UpdateById(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);\n        String sql = SqlMethod.UPDATE_BY_ID.format(tableInfo.getTableName(),\n            sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, ENTITY, ENTITY_DOT),\n            tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional);\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/methods/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 注入 SQL 操作方法相关类\n *\n * @author hubin\n * @since 2018-04-06\n */\npackage com.baomidou.mybatisplus.core.injector.methods;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/injector/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 注入核心代码\n *\n * @author hubin\n * @since 2018-04-06\n */\npackage com.baomidou.mybatisplus.core.injector;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.mapper;\n\nimport com.baomidou.mybatisplus.core.batch.BatchSqlSession;\nimport com.baomidou.mybatisplus.core.batch.MybatisBatch;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.methods.SelectList;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.SelectProvider;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.exceptions.TooManyResultsException;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.ognl.OgnlOps;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.BiPredicate;\n\n/*\n\n               :`\n                    .:,\n                     :::,,.\n             ::      `::::::\n             ::`    `,:,` .:`\n             `:: `::::::::.:`      `:';,`\n              ::::,     .:::`   `@++++++++:\n               ``        :::`  @+++++++++++#\n                         :::, #++++++++++++++`\n                 ,:      `::::::;'##++++++++++\n                 .@#@;`   ::::::::::::::::::::;\n                  #@####@, :::::::::::::::+#;::.\n                  @@######+@:::::::::::::.  #@:;\n           ,      @@########':::::::::::: .#''':`\n           ;##@@@+:##########@::::::::::: @#;.,:.\n            #@@@######++++#####'::::::::: .##+,:#`\n            @@@@@#####+++++'#####+::::::::` ,`::@#:`\n            `@@@@#####++++++'#####+#':::::::::::@.\n             @@@@######+++++''#######+##';::::;':,`\n              @@@@#####+++++'''#######++++++++++`\n               #@@#####++++++''########++++++++'\n               `#@######+++++''+########+++++++;\n                `@@#####+++++''##########++++++,\n                 @@######+++++'##########+++++#`\n                @@@@#####+++++############++++;\n              ;#@@@@@####++++##############+++,\n             @@@@@@@@@@@###@###############++'\n           @#@@@@@@@@@@@@###################+:\n        `@#@@@@@@@@@@@@@@###################'`\n      :@#@@@@@@@@@@@@@@@@@##################,\n      ,@@@@@@@@@@@@@@@@@@@@################;\n       ,#@@@@@@@@@@@@@@@@@@@##############+`\n        .#@@@@@@@@@@@@@@@@@@#############@,\n          @@@@@@@@@@@@@@@@@@@###########@,\n           :#@@@@@@@@@@@@@@@@##########@,\n            `##@@@@@@@@@@@@@@@########+,\n              `+@@@@@@@@@@@@@@@#####@:`\n                `:@@@@@@@@@@@@@@##@;.\n                   `,'@@@@##@@@+;,`\n                        ``...``\n\n _ _     /_ _ _/_. ____  /    _\n/ / //_//_//_|/ /_\\  /_///_/_\\      Talk is cheap. Show me the code.\n     _/             /\n */\n\n/**\n * Mapper 继承该接口后，无需编写 mapper.xml 文件，即可获得CRUD功能\n * <p>这个 Mapper 支持 id 泛型</p>\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic interface BaseMapper<T> extends Mapper<T> {\n\n    /**\n     * 插入一条记录\n     *\n     * @param entity 实体对象\n     */\n    int insert(T entity);\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param id 主键ID\n     */\n    default int deleteById(Serializable id) {\n        return deleteById(id, true);\n    }\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param useFill 是否填充\n     * @param obj      主键ID或实体\n     * @since 3.5.7\n     */\n    default int deleteById(Object obj, boolean useFill) {\n        Class<?> entityClass = ReflectionKit.getSuperClassGenericType(getClass(), BaseMapper.class, 0);\n        Assert.notNull(entityClass, \"entityClass must not be null\");\n        if (!entityClass.isAssignableFrom(obj.getClass()) && useFill) {\n            TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);\n            Assert.notNull(tableInfo, \"Can not get TableInfo for entity \" + entityClass);\n            String keyProperty = tableInfo.getKeyProperty();\n            Assert.notEmpty(keyProperty, \"The current table has no primary key.\");\n            if (tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {\n                T instance = tableInfo.newInstance();\n                tableInfo.setPropertyValue(instance, keyProperty, OgnlOps.convertValue(obj, tableInfo.getKeyType()));\n                return this.deleteById(instance);\n            }\n        }\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        SqlSession sqlSession = mapperProxyMetadata.getSqlSession();\n        return sqlSession.delete(mapperProxyMetadata.getMapperInterface().getName() + Constants.DOT + SqlMethod.DELETE_BY_ID.getMethod(), obj);\n    }\n\n    /**\n     * 根据实体(ID)删除\n     *\n     * @param entity 实体对象\n     * @since 3.4.4\n     */\n    int deleteById(T entity);\n\n    /**\n     * 根据 columnMap 条件，删除记录\n     *\n     * @param columnMap 表字段 map 对象\n     */\n    default int deleteByMap(Map<String, Object> columnMap) {\n        return this.delete(Wrappers.<T>query().allEq(columnMap));\n    }\n\n    /**\n     * 根据 entity 条件，删除记录\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null,里面的 entity 用于生成 where 语句）\n     */\n    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n\n    /**\n     * 删除（根据ID或实体 批量删除）\n     *\n     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)\n     * @deprecated 3.5.7 {@link #deleteByIds(Collection)}\n     */\n    @Deprecated\n    default int deleteBatchIds(@Param(Constants.COLL) Collection<?> idList) {\n        return deleteByIds(idList);\n    }\n\n\n    /**\n     * 删除（根据ID或实体 批量删除）\n     *\n     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)\n     * @since 3.5.7\n     */\n    default int deleteByIds(@Param(Constants.COLL) Collection<?> idList) {\n        return deleteByIds(idList, true);\n    }\n\n    /**\n     * 删除（根据ID或实体 批量删除）\n     * <p>\n     * 普通删除: DELETE FROM h2user WHERE id IN ( ? , ? )\n     * </p>\n     * <p>\n     * 逻辑删除: UPDATE h2user SET deleted=1 WHERE id IN ( ? , ? ) AND deleted=0\n     * </p>\n     * <p>\n     * 逻辑删除(填充): UPDATE h2user SET delete_user = 'xxx', deleted=1 WHERE id IN ( ? , ? ) AND deleted=0\n     *     <ul>注意:无论参数为id还是实体,填充参数只会以方法追加的et参数为准.<ul/>\n     * </p>\n     *\n     * @param collections 主键ID列表或实体列表(不能为 null 以及 empty)\n     * @param useFill     逻辑删除下是否填充\n     * @since 3.5.7\n     */\n    default int deleteByIds(@Param(Constants.COLL) Collection<?> collections, boolean useFill) {\n        if (CollectionUtils.isEmpty(collections)) {\n            return 0;\n        }\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        Class<?> entityClass = ReflectionKit.getSuperClassGenericType(getClass(), BaseMapper.class, 0);\n        Assert.notNull(entityClass, \"entityClass must not be null\");\n        SqlSession sqlSession = mapperProxyMetadata.getSqlSession();\n        Class<?> mapperInterface = mapperProxyMetadata.getMapperInterface();\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);\n        Assert.notNull(tableInfo, \"Can not get TableInfo for entity \" + entityClass);\n        Map<String, Object> params = new HashMap<>();\n        if (useFill && tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill()) {\n            params.put(Constants.MP_FILL_ET, tableInfo.newInstance());\n        }\n        params.put(Constants.COLL, collections);\n        return sqlSession.delete(mapperInterface.getName() + StringPool.DOT + SqlMethod.DELETE_BY_IDS.getMethod(), params);\n    }\n\n    /**\n     * 根据 ID 修改\n     *\n     * @param entity 实体对象\n     */\n    int updateById(@Param(Constants.ENTITY) T entity);\n\n    /**\n     * 根据 whereEntity 条件，更新记录\n     *\n     * @param entity        实体对象 (set 条件值,可以为 null,当entity为null时,无法进行自动填充)\n     * @param updateWrapper 实体对象封装操作类（可以为 null,里面的 entity 用于生成 where 语句）\n     */\n    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);\n\n    /**\n     * 根据 Wrapper 更新记录\n     * <p>此方法无法进行自动填充,如需自动填充请使用{@link #update(Object, Wrapper)}</p>\n     *\n     * @param updateWrapper {@link UpdateWrapper} or {@link LambdaUpdateWrapper}\n     * @since 3.5.4\n     */\n    default int update(@Param(Constants.WRAPPER) Wrapper<T> updateWrapper) {\n        return update(null, updateWrapper);\n    }\n\n    /**\n     * 根据 ID 查询\n     *\n     * @param id 主键ID\n     */\n    T selectById(Serializable id);\n\n    /**\n     * 查询（根据ID 批量查询）\n     *\n     * @param idList 主键ID列表(不能为 null 以及 empty)\n     * @return 数据列表\n     */\n    List<T> selectByIds(@Param(Constants.COLL) Collection<? extends Serializable> idList);\n\n    /**\n     * 查询（根据ID 批量查询）\n     *\n     * @param idList 主键ID列表(不能为 null 以及 empty)\n     * @return 数据列表\n     * @deprecated 3.5.8\n     */\n    default List<T> selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList) {\n        return selectByIds(idList);\n    }\n\n    /**\n     * 查询（根据ID 批量查询）\n     *\n     * @param idList        idList 主键ID列表(不能为 null 以及 empty)\n     * @param resultHandler resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.8\n     */\n    void selectByIds(@Param(Constants.COLL) Collection<? extends Serializable> idList, ResultHandler<T> resultHandler);\n\n    /**\n     * @param idList        idList 主键ID列表(不能为 null 以及 empty)\n     * @param resultHandler resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     * @deprecated 3.5.8\n     */\n    @Deprecated\n    default void selectBatchIds(@Param(Constants.COLL) Collection<? extends Serializable> idList, ResultHandler<T> resultHandler) {\n        selectByIds(idList, resultHandler);\n    }\n\n    /**\n     * 查询（根据 columnMap 条件）\n     *\n     * @param columnMap 表字段 map 对象\n     */\n    default List<T> selectByMap(Map<String, Object> columnMap) {\n        return this.selectList(Wrappers.<T>query().allEq(columnMap));\n    }\n\n    /**\n     * 查询（根据 columnMap 条件）\n     *\n     * @param columnMap     表字段 map 对象\n     * @param resultHandler resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    default void selectByMap(Map<String, Object> columnMap, ResultHandler<T> resultHandler) {\n        this.selectList(Wrappers.<T>query().allEq(columnMap), resultHandler);\n    }\n\n    /**\n     * 根据 entity 条件，查询一条记录\n     * <p>查询一条记录，例如 qw.last(\"limit 1\") 限制取一条记录, 注意：多条数据会报异常</p>\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {\n        return this.selectOne(queryWrapper, true);\n    }\n\n    /**\n     * 根据 entity 条件，查询一条记录，现在会根据{@code throwEx}参数判断是否抛出异常，如果为false就直接返回一条数据\n     * <p>查询一条记录，例如 qw.last(\"limit 1\") 限制取一条记录, 注意：多条数据会报异常</p>\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     * @param throwEx      boolean 参数，为true如果存在多个结果直接抛出异常\n     */\n    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, boolean throwEx) {\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        SqlSession sqlSession = mapperProxyMetadata.getSqlSession();\n        // 使用游标方式查询\n        try (Cursor<T> cursor = sqlSession.selectCursor(mapperProxyMetadata.getMapperInterface().getName() + Constants.DOT +\n            Constants.SELECT_WITH_CURSOR, queryWrapper)) {\n            for (T obj : cursor) {\n                // 只返回一条数据\n                return obj;\n            }\n        } catch (IOException e) {\n            if (throwEx) {\n                throw new RuntimeException(e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 根据 Wrapper 条件，游标方式查询全部记录\n     * <p>注意！需要在事务中 @Transactional 注解方法，或者手动管理 sqlSession 避免会话中断</p>\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    @SelectProvider(type = SelectList.class, method = Constants.SELECT_WITH_CURSOR)\n    Cursor<T> selectWithCursor(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 Wrapper 条件，判断是否存在记录\n     *\n     * @param queryWrapper 实体对象封装操作类\n     * @return 是否存在记录\n     */\n    default boolean exists(Wrapper<T> queryWrapper) {\n        Long count = this.selectCount(queryWrapper);\n        return null != count && count > 0;\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询总记录数\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 entity 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 entity 条件，查询全部记录\n     *\n     * @param queryWrapper  实体对象封装操作类（可以为 null）\n     * @param resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    void selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);\n\n    /**\n     * 根据 entity 条件，查询全部记录（并翻页）\n     *\n     * @param page         分页查询条件\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     * @since 3.5.3.2\n     */\n    List<T> selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 entity 条件，查询全部记录（并翻页）\n     * @param page          分页查询条件\n     * @param queryWrapper  实体对象封装操作类（可以为 null）\n     * @param resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    void selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);\n\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类\n     */\n    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper  实体对象封装操作类\n     * @param resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    void selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录（并翻页）\n     *\n     * @param page         分页查询条件\n     * @param queryWrapper 实体对象封装操作类\n     * @since 3.5.3.2\n     */\n    List<Map<String, Object>> selectMaps(IPage<? extends Map<String, Object>> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录（并翻页）\n     *\n     * @param page          分页查询条件\n     * @param queryWrapper  实体对象封装操作类\n     * @param resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    void selectMaps(IPage<? extends Map<String, Object>> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     * <p>注意： 只返回第一个字段的值</p>\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    <E> List<E> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     * <p>注意： 只返回第一个字段的值</p>\n     *\n     * @param queryWrapper  实体对象封装操作类（可以为 null）\n     * @param resultHandler 结果处理器 {@link ResultHandler}\n     * @since 3.5.4\n     */\n    <E> void selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<E> resultHandler);\n\n    /**\n     * 根据 entity 条件，查询全部记录（并翻页）\n     *\n     * @param page         分页查询条件\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    default <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {\n        page.setRecords(selectList(page, queryWrapper));\n        return page;\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录（并翻页）\n     *\n     * @param page         分页查询条件\n     * @param queryWrapper 实体对象封装操作类\n     */\n    default <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {\n        page.setRecords(selectMaps(page, queryWrapper));\n        return page;\n    }\n\n    /**\n     * 主键存在更新记录，否插入一条记录\n     *\n     * @param entity 实体对象 (不能为空)\n     * @since 3.5.7\n     */\n    default boolean insertOrUpdate(T entity) {\n        Class<?> entityClass = ReflectionKit.getSuperClassGenericType(getClass(), BaseMapper.class, 0);\n        Assert.notNull(entityClass, \"entityClass must not be null\");\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);\n        Assert.notNull(tableInfo, \"Can not get TableInfo for entity \" + entityClass);\n        String keyProperty = tableInfo.getKeyProperty();\n        Assert.notEmpty(keyProperty, \"The current table has no primary key.\");\n        Object idVal = tableInfo.getPropertyValue(entity, keyProperty);\n        return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ? insert(entity) > 0 : updateById(entity) > 0;\n    }\n\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     * @since 3.5.7\n     */\n    default List<BatchResult> insert(Collection<T> entityList) {\n        return insert(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     * @since 3.5.7\n     */\n    default List<BatchResult> insert(Collection<T> entityList, int batchSize) {\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        MybatisBatch.Method<T> method = new MybatisBatch.Method<>(mapperProxyMetadata.getMapperInterface());\n        SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mapperProxyMetadata.getSqlSession());\n        return MybatisBatchUtils.execute(sqlSessionFactory, entityList, method.insert(), batchSize);\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     * @since 3.5.7\n     */\n    default List<BatchResult> updateById(Collection<T> entityList) {\n        return updateById(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     * @since 3.5.7\n     */\n    default List<BatchResult> updateById(Collection<T> entityList, int batchSize) {\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        MybatisBatch.Method<T> method = new MybatisBatch.Method<>(mapperProxyMetadata.getMapperInterface());\n        SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mapperProxyMetadata.getSqlSession());\n        return MybatisBatchUtils.execute(sqlSessionFactory, entityList, method.updateById(), batchSize);\n    }\n\n    /**\n     * 批量修改或插入\n     *\n     * @param entityList 实体对象集合\n     * @since 3.5.7\n     */\n    default List<BatchResult> insertOrUpdate(Collection<T> entityList) {\n        return insertOrUpdate(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 批量修改或插入\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     * @since 3.5.7\n     */\n    default List<BatchResult> insertOrUpdate(Collection<T> entityList, int batchSize) {\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        Class<?> entityClass = ReflectionKit.getSuperClassGenericType(getClass(), BaseMapper.class, 0);\n        Assert.notNull(entityClass, \"entityClass must not be null\");\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);\n        Assert.notNull(tableInfo, \"Can not get TableInfo for entity \" + entityClass);\n        String keyProperty = tableInfo.getKeyProperty();\n        Assert.notEmpty(keyProperty, \"The current table has no primary key.\");\n        String statement = mapperProxyMetadata.getMapperInterface().getName() + StringPool.DOT + SqlMethod.SELECT_BY_ID.getMethod();\n        return insertOrUpdate(entityList, (sqlSession, entity) -> {\n            Object idVal = tableInfo.getPropertyValue(entity, keyProperty);\n            return StringUtils.checkValNull(idVal) || CollectionUtils.isEmpty(sqlSession.selectList(statement, entity));\n        }, batchSize);\n    }\n\n    /**\n     * 批量修改或插入\n     *\n     * @param entityList 实体对象集合\n     * @since 3.5.7\n     */\n    default List<BatchResult> insertOrUpdate(Collection<T> entityList, BiPredicate<BatchSqlSession, T> insertPredicate) {\n        return insertOrUpdate(entityList, insertPredicate, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 批量修改或插入\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     * @since 3.5.7\n     */\n    default List<BatchResult> insertOrUpdate(Collection<T> entityList, BiPredicate<BatchSqlSession, T> insertPredicate, int batchSize) {\n        MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this);\n        MybatisBatch.Method<T> method = new MybatisBatch.Method<>(mapperProxyMetadata.getMapperInterface());\n        SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mapperProxyMetadata.getSqlSession());\n        return MybatisBatchUtils.saveOrUpdate(sqlSessionFactory, entityList, method.insert(), insertPredicate, method.updateById(), batchSize);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/Mapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.mapper;\n\n/**\n * 顶级Mapper\n *\n * @author nieqiurong 2019/4/13.\n */\npublic interface Mapper<T> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 注入 mapper 基类\n */\npackage com.baomidou.mybatisplus.core.mapper;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/IPage.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport static java.util.stream.Collectors.toList;\n\n/**\n * 分页 Page 对象接口\n *\n * @author hubin\n * @since 2018-06-09\n */\npublic interface IPage<T> extends Serializable {\n\n    /**\n     * 获取排序信息，排序的字段和正反序\n     *\n     * @return 排序信息\n     */\n    List<OrderItem> orders();\n\n    /**\n     * 自动优化 COUNT SQL【 默认：true 】\n     *\n     * @return true 是 / false 否\n     */\n    default boolean optimizeCountSql() {\n        return true;\n    }\n\n    /**\n     * {@link com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#isOptimizeJoin()}\n     * 两个参数都为 true 才会进行sql处理\n     *\n     * @return true 是 / false 否\n     * @since 3.4.4 @2021-09-13\n     */\n    default boolean optimizeJoinOfCountSql() {\n        return true;\n    }\n\n    /**\n     * 进行 count 查询 【 默认: true 】\n     *\n     * @return true 是 / false 否\n     */\n    default boolean searchCount() {\n        return true;\n    }\n\n    /**\n     * 计算当前分页偏移量\n     */\n    default long offset() {\n        long current = getCurrent();\n        if (current <= 1L) {\n            return 0L;\n        }\n        return Math.max((current - 1) * getSize(), 0L);\n    }\n\n    /**\n     * 最大每页分页数限制,优先级高于分页插件内的 maxLimit\n     *\n     * @since 3.4.0 @2020-07-17\n     */\n    default Long maxLimit() {\n        return null;\n    }\n\n    /**\n     * 当前分页总页数\n     */\n    default long getPages() {\n        if (getSize() == 0) {\n            return 0L;\n        }\n        long pages = getTotal() / getSize();\n        if (getTotal() % getSize() != 0) {\n            pages++;\n        }\n        return pages;\n    }\n\n    /**\n     * 内部什么也不干\n     * <p>只是为了 json 反序列化时不报错</p>\n     * @deprecated 3.5.8\n     */\n    @Deprecated\n    default IPage<T> setPages(long pages) {\n        // to do nothing\n        return this;\n    }\n\n    /**\n     * 分页记录列表\n     *\n     * @return 分页对象记录列表\n     */\n    List<T> getRecords();\n\n    /**\n     * 设置分页记录列表\n     */\n    IPage<T> setRecords(List<T> records);\n\n    /**\n     * 当前满足条件总行数\n     *\n     * @return 总条数\n     */\n    long getTotal();\n\n    /**\n     * 设置当前满足条件总行数\n     */\n    IPage<T> setTotal(long total);\n\n    /**\n     * 获取每页显示条数\n     *\n     * @return 每页显示条数\n     */\n    long getSize();\n\n    /**\n     * 设置每页显示条数\n     */\n    IPage<T> setSize(long size);\n\n    /**\n     * 当前页\n     *\n     * @return 当前页\n     */\n    long getCurrent();\n\n    /**\n     * 设置当前页\n     */\n    IPage<T> setCurrent(long current);\n\n    /**\n     * IPage 的泛型转换\n     *\n     * @param mapper 转换函数\n     * @param <R>    转换后的泛型\n     * @return 转换泛型后的 IPage\n     */\n    @SuppressWarnings(\"unchecked\")\n    default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {\n        List<R> collect = this.getRecords().stream().map(mapper).collect(toList());\n        return ((IPage<R>) this).setRecords(collect);\n    }\n\n    /**\n     * 老分页插件不支持\n     * <p>\n     * MappedStatement 的 id\n     *\n     * @return id\n     * @since 3.4.0 @2020-06-19\n     */\n    default String countId() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/MapperProxyMetadata.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.SqlSession;\n\n/**\n * Mapper代理属性\n *\n * @author nieqiurong\n * @see com.baomidou.mybatisplus.core.override.MybatisMapperProxy\n * @see org.apache.ibatis.binding.MapperProxy\n * @since 3.5.12\n */\n@SuppressWarnings(\"LombokGetterMayBeUsed\")\npublic class MapperProxyMetadata {\n\n    private final SqlSession sqlSession;\n\n    private final Class<?> mapperInterface;\n\n    public MapperProxyMetadata(MetaObject metaObject) {\n        if (!metaObject.hasGetter(\"mapperInterface\") || !metaObject.hasGetter(\"sqlSession\")) {\n            throw new MybatisPlusException(\"Unable to retrieve the mapperInterface and sqlSession properties from \" + metaObject.getOriginalObject());\n        }\n        this.mapperInterface = (Class<?>) metaObject.getValue(\"mapperInterface\");\n        this.sqlSession = (SqlSession) metaObject.getValue(\"sqlSession\");\n    }\n\n    public Class<?> getMapperInterface() {\n        return mapperInterface;\n    }\n\n    public SqlSession getSqlSession() {\n        return sqlSession;\n    }\n\n    @Override\n    public String toString() {\n        return \"MapperProxy{\" +\n            \"mapperInterface=\" + mapperInterface +\n            \", sqlSession=\" + sqlSession +\n            '}';\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/OrderFieldInfo.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport lombok.Data;\n\n/**\n * @author nieqiurong\n * @since 3.5.4\n */\n@Data\npublic class OrderFieldInfo {\n\n    /**\n     * 字段\n     */\n    private String column;\n\n    /**\n     * 排序类型\n     */\n    private String type;\n\n    /**\n     * 排序顺序\n     */\n    private short sort;\n\n\n    public OrderFieldInfo(String column, boolean asc, short orderBySort) {\n        this.column = column;\n        this.type = asc ? Constants.ASC : Constants.DESC;\n        this.sort = orderBySort;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/OrderItem.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.io.Serializable;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 排序元素载体\n *\n * @author HCL\n * Create at 2019/5/27\n */\n@Getter\n@Setter\npublic class OrderItem implements Serializable {\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 需要进行排序的字段\n     */\n    private String column;\n    /**\n     * 是否正序排列，默认 true\n     */\n    private boolean asc = true;\n\n    public OrderItem() {\n    }\n\n    /**\n     * @since 3.5.13\n     * @param column 字段\n     * @param asc 是否升序\n     */\n    private OrderItem(String column, boolean asc) {\n        this.column = column;\n        this.asc = asc;\n    }\n\n    /**\n     * 根据指定字段升序排序\n     * @param column 数据库字段\n     * @return 排序字段\n     */\n    public static OrderItem asc(String column) {\n        return build(column, true);\n    }\n\n    /**\n     * 根据指定字段降序排序\n     * @param column 数据库字段\n     * @return 排序字段\n     */\n    public static OrderItem desc(String column) {\n        return build(column, false);\n    }\n\n    /**\n     * 根据表达式排序\n     * <p>任意表达式语句,自行控制SQL注入</p>\n     * <p>不适用与反序列</p>\n     * @since 3.5.13\n     * @param expression 字段表达式\n     * @return 排序字段\n     */\n    public static OrderItem withExpression(String expression) {\n        return withExpression(expression, false);\n    }\n\n    /**\n     * 根据表达式排序\n     * <p>任意表达式语句,自行控制SQL注入</p>\n     * <p>不适用与反序列</p>\n     * @since 3.5.13\n     * @param expression 字段表达式\n     * @param asc 是否正序\n     * @return 排序字段\n     */\n    public static OrderItem withExpression(String expression, boolean asc) {\n        return new OrderItem(expression, asc);\n    }\n\n    /**\n     * 根据指定字段列表进行升序排序\n     * @param columns 字段列表\n     * @return 排序字段\n     */\n    public static List<OrderItem> ascs(String... columns) {\n        return Arrays.stream(columns).map(OrderItem::asc).collect(Collectors.toList());\n    }\n\n    /**\n     * 根据指定字段列表进行降序排序\n     * @param columns 字段列表\n     * @return 排序字段\n     */\n    public static List<OrderItem> descs(String... columns) {\n        return Arrays.stream(columns).map(OrderItem::desc).collect(Collectors.toList());\n    }\n\n    private static OrderItem build(String column, boolean asc) {\n        return new OrderItem().setColumn(column).setAsc(asc);\n    }\n\n    public OrderItem setColumn(String column) {\n        // TODO 反序列化会到这里被处理,后期重构需要改动\n        this.column = StringUtils.replaceAllBlank(column);\n        return this;\n    }\n\n    public OrderItem setAsc(boolean asc) {\n        this.asc = asc;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderItem{\" +\n            \"column='\" + column + '\\'' +\n            \", asc=\" + asc +\n            '}';\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableFieldInfo.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport lombok.*;\nimport org.apache.ibatis.mapping.ResultMapping;\nimport org.apache.ibatis.reflection.Reflector;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.*;\n\nimport java.lang.reflect.Field;\nimport java.util.Map;\n\n/**\n * 数据库表字段反射信息\n *\n * @author hubin sjy willenfoo tantan\n * @since 2016-09-09\n */\n@Getter\n@ToString\n@EqualsAndHashCode\npublic class TableFieldInfo implements Constants {\n\n    /**\n     * 属性\n     *\n     * @since 3.3.1\n     */\n    private final Field field;\n    /**\n     * 字段名\n     */\n    private final String column;\n    /**\n     * 属性名\n     */\n    private final String property;\n    /**\n     * 属性表达式#{property}, 可以指定jdbcType, typeHandler等\n     */\n    private final String el;\n    /**\n     * jdbcType, typeHandler等部分\n     */\n    private final String mapping;\n    /**\n     * 属性类型\n     */\n    private final Class<?> propertyType;\n    /**\n     * 是否是基本数据类型\n     *\n     * @since 3.4.0 @2020-6-19\n     */\n    private final boolean isPrimitive;\n    /**\n     * 属性是否是 CharSequence 类型\n     */\n    private final boolean isCharSequence;\n    /**\n     * 字段验证策略之 insert\n     * Refer to {@link TableField#insertStrategy()}\n     *\n     * @since added v_3.1.2 @2019-5-7\n     */\n    private final FieldStrategy insertStrategy;\n    /**\n     * 字段验证策略之 update\n     * Refer to {@link TableField#updateStrategy()}\n     *\n     * @since added v_3.1.2 @2019-5-7\n     */\n    private final FieldStrategy updateStrategy;\n    /**\n     * 字段验证策略之 where\n     * Refer to {@link TableField#whereStrategy()}\n     *\n     * @since added v_3.1.2 @2019-5-7\n     */\n    private final FieldStrategy whereStrategy;\n    /**\n     * 是否是乐观锁字段\n     */\n    private final boolean version;\n    /**\n     * 是否进行 select 查询\n     * <p>大字段可设置为 false 不加入 select 查询范围</p>\n     */\n    private boolean select = true;\n    /**\n     * 是否是逻辑删除字段\n     */\n    @Getter\n    private boolean logicDelete = false;\n    /**\n     * 逻辑删除值\n     */\n    private String logicDeleteValue;\n    /**\n     * 逻辑未删除值\n     */\n    private String logicNotDeleteValue;\n    /**\n     * 字段 update set 部分注入\n     */\n    private String update;\n    /**\n     * where 字段比较条件\n     */\n    private String condition = SqlCondition.EQUAL;\n    /**\n     * 字段填充策略\n     */\n    private FieldFill fieldFill = FieldFill.DEFAULT;\n    /**\n     * 表字段是否启用了插入填充\n     *\n     * @since 3.3.0\n     */\n    private boolean withInsertFill;\n    /**\n     * 表字段是否启用了更新填充\n     *\n     * @since 3.3.0\n     */\n    private boolean withUpdateFill;\n    /**\n     * 缓存 sql select\n     */\n    @Setter(AccessLevel.NONE)\n    private String sqlSelect;\n    /**\n     * JDBC类型\n     *\n     * @since 3.1.2\n     */\n    private JdbcType jdbcType;\n    /**\n     * 类型处理器\n     *\n     * @since 3.1.2\n     */\n    private Class<? extends TypeHandler<?>> typeHandler;\n\n    /**\n     * 是否存在OrderBy注解\n     */\n    private boolean isOrderBy;\n    /**\n     * 排序类型\n     */\n    private String orderByType;\n    /**\n     * 排序顺序\n     */\n    private short orderBySort;\n\n    /**\n     * 全新的 存在 TableField 注解时使用的构造函数\n     */\n    public TableFieldInfo(GlobalConfig globalConfig, TableInfo tableInfo, Field field, TableField tableField,\n                          Reflector reflector, boolean existTableLogic, boolean isOrderBy) {\n        this(globalConfig, tableInfo, field, tableField, reflector, existTableLogic);\n        this.isOrderBy = isOrderBy;\n        if (isOrderBy) {\n            initOrderBy(tableInfo, globalConfig.getAnnotationHandler().getAnnotation(field, OrderBy.class));\n        }\n    }\n\n    /**\n     * 全新的 存在 TableField 注解时使用的构造函数\n     */\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    public TableFieldInfo(GlobalConfig globalConfig, TableInfo tableInfo, Field field, TableField tableField,\n                          Reflector reflector, boolean existTableLogic) {\n\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        field.setAccessible(true);\n        this.field = field;\n        this.version = globalConfig.getAnnotationHandler().isAnnotationPresent(field, Version.class);\n        this.property = field.getName();\n        this.propertyType = reflector.getGetterType(this.property);\n        this.isPrimitive = this.propertyType.isPrimitive();\n        this.isCharSequence = StringUtils.isCharSequence(this.propertyType);\n        this.fieldFill = tableField.fill();\n        this.withInsertFill = this.fieldFill == FieldFill.INSERT || this.fieldFill == FieldFill.INSERT_UPDATE;\n        this.withUpdateFill = this.fieldFill == FieldFill.UPDATE || this.fieldFill == FieldFill.INSERT_UPDATE;\n        this.update = tableField.update();\n        JdbcType jdbcType = tableField.jdbcType();\n        final Class<? extends TypeHandler> typeHandler = tableField.typeHandler();\n        final String numericScale = tableField.numericScale();\n        boolean needAs = false;\n        String el = this.property;\n        if (StringUtils.isNotBlank(tableField.property())) {\n            el = tableField.property();\n            needAs = true;\n        }\n        if (JdbcType.UNDEFINED != jdbcType) {\n            this.jdbcType = jdbcType;\n            el += (COMMA + SqlScriptUtils.mappingJdbcType(jdbcType));\n        }\n        if (UnknownTypeHandler.class != typeHandler) {\n            this.typeHandler = (Class<? extends TypeHandler<?>>) typeHandler;\n            if (tableField.javaType()) {\n                String javaType = null;\n                TypeAliasRegistry registry = tableInfo.getConfiguration().getTypeAliasRegistry();\n                Map<String, Class<?>> typeAliases = registry.getTypeAliases();\n                for (Map.Entry<String, Class<?>> entry : typeAliases.entrySet()) {\n                    if (entry.getValue().equals(propertyType)) {\n                        javaType = entry.getKey();\n                        break;\n                    }\n                }\n                if (javaType == null) {\n                    javaType = propertyType.getName();\n                    registry.registerAlias(javaType, propertyType);\n                }\n                el += (COMMA + \"javaType=\" + javaType);\n            }\n            el += (COMMA + SqlScriptUtils.mappingTypeHandler(this.typeHandler));\n        }\n        if (StringUtils.isNotBlank(numericScale)) {\n            el += (COMMA + SqlScriptUtils.mappingNumericScale(Integer.valueOf(numericScale)));\n        }\n        this.el = el;\n        int index = el.indexOf(COMMA);\n        this.mapping = index > 0 ? el.substring(++index) : null;\n        this.initLogicDelete(globalConfig, field, existTableLogic);\n\n        String column = tableField.value();\n        if (StringUtils.isBlank(column)) {\n            column = this.property;\n            if (tableInfo.isUnderCamel()) {\n                /* 开启字段下划线申明 */\n                column = StringUtils.camelToUnderline(column);\n            }\n            if (dbConfig.isCapitalMode()) {\n                /* 开启字段全大写申明 */\n                column = column.toUpperCase();\n            }\n            String columnFormat = dbConfig.getColumnFormat();\n            if (StringUtils.isNotBlank(columnFormat)) {\n                column = String.format(columnFormat, column);\n            }\n        } else {\n            String columnFormat = dbConfig.getColumnFormat();\n            if (StringUtils.isNotBlank(columnFormat) && tableField.keepGlobalFormat()) {\n                column = String.format(columnFormat, column);\n            }\n        }\n        this.column = column;\n        this.sqlSelect = column;\n        if (needAs) {\n            // 存在指定转换属性\n            String propertyFormat = dbConfig.getPropertyFormat();\n            if (StringUtils.isBlank(propertyFormat)) {\n                propertyFormat = \"%s\";\n            }\n            this.sqlSelect += (AS + String.format(propertyFormat, tableField.property()));\n        } else if (tableInfo.getResultMap() == null && !tableInfo.isAutoInitResultMap() &&\n            TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {\n            /* 未设置 resultMap 也未开启自动构建 resultMap, 字段规则又不符合 mybatis 的自动封装规则 */\n            String propertyFormat = dbConfig.getPropertyFormat();\n            String asProperty = this.property;\n            if (StringUtils.isNotBlank(propertyFormat)) {\n                asProperty = String.format(propertyFormat, this.property);\n            }\n            this.sqlSelect += (AS + asProperty);\n        }\n\n        this.insertStrategy = this.chooseFieldStrategy(tableField.insertStrategy(), dbConfig.getInsertStrategy());\n        this.updateStrategy = this.chooseFieldStrategy(tableField.updateStrategy(), dbConfig.getUpdateStrategy());\n        this.whereStrategy = this.chooseFieldStrategy(tableField.whereStrategy(), dbConfig.getWhereStrategy());\n\n        if (StringUtils.isNotBlank(tableField.condition())) {\n            // 细粒度条件控制\n            this.condition = tableField.condition();\n        }\n\n        // 字段是否注入查询\n        this.select = tableField.select();\n    }\n\n    /**\n     * 优先使用单个字段注解，否则使用全局配置\n     */\n    private FieldStrategy chooseFieldStrategy(FieldStrategy fromAnnotation, FieldStrategy fromDbConfig) {\n        return fromAnnotation == FieldStrategy.DEFAULT ? fromDbConfig : fromAnnotation;\n    }\n\n    /**\n     * 不存在 TableField 注解时, 使用的构造函数\n     */\n    public TableFieldInfo(GlobalConfig globalConfig, TableInfo tableInfo, Field field, Reflector reflector,\n                          boolean existTableLogic, boolean isOrderBy) {\n        this(globalConfig, tableInfo, field, reflector, existTableLogic);\n        this.isOrderBy = isOrderBy;\n        if (isOrderBy) {\n            initOrderBy(tableInfo, globalConfig.getAnnotationHandler().getAnnotation(field, OrderBy.class));\n        }\n    }\n\n    /**\n     * 不存在 TableField 注解时, 使用的构造函数\n     */\n    public TableFieldInfo(GlobalConfig globalConfig, TableInfo tableInfo, Field field, Reflector reflector,\n                          boolean existTableLogic) {\n        field.setAccessible(true);\n        this.field = field;\n        this.version = globalConfig.getAnnotationHandler().isAnnotationPresent(field, Version.class);\n        this.property = field.getName();\n        this.propertyType = reflector.getGetterType(this.property);\n        this.isPrimitive = this.propertyType.isPrimitive();\n        this.isCharSequence = StringUtils.isCharSequence(this.propertyType);\n        this.el = this.property;\n        this.mapping = null;\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        this.insertStrategy = dbConfig.getInsertStrategy();\n        this.updateStrategy = dbConfig.getUpdateStrategy();\n        this.whereStrategy = dbConfig.getWhereStrategy();\n        this.initLogicDelete(globalConfig, field, existTableLogic);\n\n        String column = this.property;\n        if (tableInfo.isUnderCamel()) {\n            /* 开启字段下划线申明 */\n            column = StringUtils.camelToUnderline(column);\n        }\n        if (dbConfig.isCapitalMode()) {\n            /* 开启字段全大写申明 */\n            column = column.toUpperCase();\n        }\n\n        String columnFormat = dbConfig.getColumnFormat();\n        if (StringUtils.isNotBlank(columnFormat)) {\n            column = String.format(columnFormat, column);\n        }\n\n        this.column = column;\n        this.sqlSelect = column;\n        if (tableInfo.getResultMap() == null && !tableInfo.isAutoInitResultMap() &&\n            TableInfoHelper.checkRelated(tableInfo.isUnderCamel(), this.property, this.column)) {\n            /* 未设置 resultMap 也未开启自动构建 resultMap, 字段规则又不符合 mybatis 的自动封装规则 */\n            String propertyFormat = dbConfig.getPropertyFormat();\n            String asProperty = this.property;\n            if (StringUtils.isNotBlank(propertyFormat)) {\n                asProperty = String.format(propertyFormat, this.property);\n            }\n            this.sqlSelect += (AS + asProperty);\n        }\n    }\n\n    /**\n     * 排序初始化\n     *\n     * @param tableInfo 表信息\n     * @param orderBy   排序注解\n     */\n    private void initOrderBy(TableInfo tableInfo, OrderBy orderBy) {\n        if (null != orderBy) {\n            this.isOrderBy = true;\n            this.orderBySort = orderBy.sort();\n            this.orderByType = orderBy.asc() ? Constants.ASC : Constants.DESC;\n            tableInfo.getOrderByFields().add(new OrderFieldInfo(this.getColumn(), orderBy.asc(), orderBy.sort()));\n        } else {\n            this.isOrderBy = false;\n        }\n    }\n\n    /**\n     * 逻辑删除初始化\n     *\n     * @param globalConfig 全局配置\n     * @param field        字段属性对象\n     */\n    private void initLogicDelete(GlobalConfig globalConfig, Field field, boolean existTableLogic) {\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        /* 获取注解属性，逻辑处理字段 */\n        TableLogic tableLogic = globalConfig.getAnnotationHandler().getAnnotation(field, TableLogic.class);\n        if (null != tableLogic) {\n            if (StringUtils.isNotBlank(tableLogic.value())) {\n                this.logicNotDeleteValue = tableLogic.value();\n            } else {\n                this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();\n            }\n            if (StringUtils.isNotBlank(tableLogic.delval())) {\n                this.logicDeleteValue = tableLogic.delval();\n            } else {\n                this.logicDeleteValue = dbConfig.getLogicDeleteValue();\n            }\n            this.logicDelete = true;\n        } else if (!existTableLogic) {\n            String deleteField = dbConfig.getLogicDeleteField();\n            if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {\n                this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();\n                this.logicDeleteValue = dbConfig.getLogicDeleteValue();\n                this.logicDelete = true;\n            }\n        }\n    }\n\n    /**\n     * 获取 insert 时候插入值 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"值\" 部位</p>\n     *\n     * <li> 不生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getInsertSqlProperty(final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        return SqlScriptUtils.safeParam(newPrefix + el) + COMMA;\n    }\n\n    /**\n     * 获取 insert 时候插入值 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"值\" 部位</p>\n     *\n     * <li> 根据规则会生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getInsertSqlPropertyMaybeIf(final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        String sqlScript = getInsertSqlProperty(newPrefix);\n        if (withInsertFill) {\n            return sqlScript;\n        }\n        return convertIf(sqlScript, newPrefix + property, insertStrategy);\n    }\n\n    /**\n     * 获取 insert 时候字段 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"字段\" 部位</p>\n     *\n     * <li> 不生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getInsertSqlColumn() {\n        return column + COMMA;\n    }\n\n    /**\n     * 获取 insert 时候字段 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"字段\" 部位</p>\n     *\n     * <li> 根据规则会生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getInsertSqlColumnMaybeIf(final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        final String sqlScript = getInsertSqlColumn();\n        if (withInsertFill) {\n            return sqlScript;\n        }\n        return convertIf(sqlScript, newPrefix + property, insertStrategy);\n    }\n\n    /**\n     * 获取 set sql 片段\n     *\n     * @param prefix 前缀\n     * @return sql 脚本片段\n     */\n    public String getSqlSet(final String prefix) {\n        return getSqlSet(false, prefix);\n    }\n\n    /**\n     * 获取 set sql 片段\n     *\n     * @param ignoreIf 忽略 IF 包裹\n     * @param prefix   前缀\n     * @return sql 脚本片段\n     */\n    public String getSqlSet(final boolean ignoreIf, final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        // 默认: column=\n        String sqlSet = column + EQUALS;\n        if (StringUtils.isNotBlank(update)) {\n            sqlSet += String.format(update, column);\n        } else {\n            sqlSet += SqlScriptUtils.safeParam(newPrefix + el);\n        }\n        sqlSet += COMMA;\n        if (ignoreIf) {\n            return sqlSet;\n        }\n        if (withUpdateFill) {\n            // 不进行 if 包裹\n            return sqlSet;\n        }\n        return convertIf(sqlSet, convertIfProperty(newPrefix, property), updateStrategy);\n    }\n\n    private String convertIfProperty(String prefix, String property) {\n        return StringUtils.isNotBlank(prefix) ? prefix.substring(0, prefix.length() - 1) + \"['\" + property + \"']\" : property;\n    }\n\n    /**\n     * 获取 查询的 sql 片段\n     *\n     * @param prefix 前缀\n     * @return sql 脚本片段\n     */\n    public String getSqlWhere(final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        // 默认:  AND column=#{prefix + el}\n        String sqlScript = \" AND \" + String.format(condition, column, newPrefix + el);\n        // 查询的时候只判非空\n        return convertIf(sqlScript, convertIfProperty(newPrefix, property), whereStrategy);\n    }\n\n    /**\n     * 获取 ResultMapping\n     *\n     * @param configuration MybatisConfiguration\n     * @return ResultMapping\n     */\n    ResultMapping getResultMapping(final Configuration configuration) {\n        ResultMapping.Builder builder = new ResultMapping.Builder(configuration, this.property,\n            StringUtils.getTargetColumn(this.column), this.propertyType);\n        TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();\n        if (this.jdbcType != null && this.jdbcType != JdbcType.UNDEFINED) {\n            builder.jdbcType(this.jdbcType);\n        }\n        if (this.typeHandler != null && this.typeHandler != UnknownTypeHandler.class) {\n            TypeHandler<?> typeHandler = registry.getMappingTypeHandler(this.typeHandler);\n            if (IJsonTypeHandler.class.isAssignableFrom(this.typeHandler)) {\n                // 保证每次实例化\n                typeHandler = MybatisUtils.newJsonTypeHandler(this.typeHandler, this.propertyType, this.field);\n            } else {\n                if (typeHandler == null) {\n                    typeHandler = registry.getInstance(this.propertyType, this.typeHandler);\n                }\n            }\n            builder.typeHandler(typeHandler);\n        }\n        return builder.build();\n    }\n\n    public String getVersionOli(final String alias, final String prefix) {\n        final String oli = \" AND \" + column + EQUALS + SqlScriptUtils.safeParam(MP_OPTLOCK_VERSION_ORIGINAL);\n        final String ognlStr = convertIfProperty(prefix, property);\n        if (isCharSequence) {\n            return SqlScriptUtils.convertIf(oli, String.format(\"%s != null and %s != null and %s != ''\", alias, ognlStr, ognlStr), false);\n        } else {\n            return SqlScriptUtils.convertIf(oli, String.format(\"%s != null and %s != null\", alias, ognlStr), false);\n        }\n    }\n\n    /**\n     * 转换成 if 标签的脚本片段\n     *\n     * @param sqlScript     sql 脚本片段\n     * @param property      字段名\n     * @param fieldStrategy 验证策略\n     * @return if 脚本片段\n     */\n    private String convertIf(final String sqlScript, final String property, final FieldStrategy fieldStrategy) {\n        if (fieldStrategy == FieldStrategy.NEVER) {\n            return null;\n        }\n        if (isPrimitive || fieldStrategy == FieldStrategy.ALWAYS) {\n            return sqlScript;\n        }\n        if (fieldStrategy == FieldStrategy.NOT_EMPTY && isCharSequence) {\n            return SqlScriptUtils.convertIf(sqlScript, String.format(\"%s != null and %s != ''\", property, property),\n                false);\n        }\n        return SqlScriptUtils.convertIf(sqlScript, String.format(\"%s != null\", property), false);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableInfo.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.KeySequence;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport lombok.AccessLevel;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.experimental.Accessors;\nimport org.apache.ibatis.mapping.ResultFlag;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.mapping.ResultMapping;\nimport org.apache.ibatis.reflection.Reflector;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.*;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * 数据库表反射信息\n *\n * @author hubin\n * @since 2016-01-23\n */\n@Data\n@Setter(AccessLevel.PACKAGE)\n@Accessors(chain = true)\npublic class TableInfo implements Constants {\n\n    /**\n     * 实体类型\n     */\n    private Class<?> entityType;\n    /**\n     * 表主键ID 类型\n     */\n    private IdType idType = IdType.NONE;\n    /**\n     * 表名称\n     */\n    private String tableName;\n    /**\n     * 表映射结果集\n     */\n    private String resultMap;\n    /**\n     * 是否是需要自动生成的 resultMap\n     */\n    private boolean autoInitResultMap;\n    /**\n     * 主键是否有存在字段名与属性名关联\n     * <p>true: 表示要进行 as</p>\n     */\n    private boolean keyRelated;\n    /**\n     * 表主键ID 字段名\n     */\n    private String keyColumn;\n    /**\n     * 表主键ID 属性名\n     */\n    private String keyProperty;\n    /**\n     * 表主键ID 属性类型\n     */\n    private Class<?> keyType;\n    /**\n     * 表主键ID Sequence\n     */\n    private KeySequence keySequence;\n    /**\n     * 表字段信息列表\n     */\n    private List<TableFieldInfo> fieldList;\n    /**\n     * 命名空间 (对应的 mapper 接口的全类名)\n     */\n    private String currentNamespace;\n    /**\n     * MybatisConfiguration 标记 (Configuration内存地址值)\n     *\n     * @deprecated 3.5.3.2 初始化阶段可以使用一下,后期尽量避免在容器初始化完成之后再继续调用此方法\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    @Deprecated\n    private Configuration configuration;\n    /**\n     * 是否开启下划线转驼峰\n     * <p>\n     * 未注解指定字段名的情况下,用于自动从 property 推算 column 的命名\n     */\n    private boolean underCamel;\n    /**\n     * 缓存包含主键及字段的 sql select\n     */\n    @Setter(AccessLevel.NONE)\n    @Getter(AccessLevel.NONE)\n    private String allSqlSelect;\n    /**\n     * 缓存主键字段的 sql select\n     */\n    @Setter(AccessLevel.NONE)\n    @Getter(AccessLevel.NONE)\n    private String sqlSelect;\n    /**\n     * 表字段是否启用了插入填充\n     *\n     * @since 3.3.0\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private boolean withInsertFill;\n    /**\n     * 表字段是否启用了更新填充\n     *\n     * @since 3.3.0\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private boolean withUpdateFill;\n    /**\n     * 表字段是否启用了逻辑删除\n     *\n     * @since 3.4.0\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private boolean withLogicDelete;\n    /**\n     * 逻辑删除字段\n     *\n     * @since 3.4.0\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private TableFieldInfo logicDeleteFieldInfo;\n    /**\n     * 表字段是否启用了乐观锁\n     *\n     * @since 3.3.1\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private boolean withVersion;\n    /**\n     * 乐观锁字段\n     *\n     * @since 3.3.1\n     */\n    @Getter\n    @Setter(AccessLevel.NONE)\n    private TableFieldInfo versionFieldInfo;\n\n    /**\n     * 排序列表\n     */\n    @Setter\n    private List<OrderFieldInfo> orderByFields;\n\n    /**\n     * @since 3.4.4\n     */\n    @Getter\n    private Reflector reflector;\n\n    /**\n     * @param configuration 配置对象\n     * @param entityType    实体类型\n     * @since 3.4.4\n     */\n    public TableInfo(Configuration configuration, Class<?> entityType) {\n        this.configuration = configuration;\n        this.entityType = entityType;\n        this.reflector = configuration.getReflectorFactory().findForClass(entityType);\n        this.underCamel = configuration.isMapUnderscoreToCamelCase();\n    }\n\n    /**\n     * 获得注入的 SQL Statement\n     *\n     * @param sqlMethod MybatisPlus 支持 SQL 方法\n     * @return SQL Statement\n     * @deprecated 3.4.0 如果存在的多mapper共用一个实体的情况，这里可能会出现获取命名空间错误的情况\n     */\n    @Deprecated\n    public String getSqlStatement(String sqlMethod) {\n        return currentNamespace + DOT + sqlMethod;\n    }\n\n    /**\n     * 是否有主键\n     *\n     * @return 是否有\n     */\n    public boolean havePK() {\n        return StringUtils.isNotBlank(keyColumn);\n    }\n\n    /**\n     * 获取主键的 select sql 片段\n     *\n     * @return sql 片段\n     */\n    public String getKeySqlSelect() {\n        if (sqlSelect != null) {\n            return sqlSelect;\n        }\n        if (havePK()) {\n            sqlSelect = keyColumn;\n            if (resultMap == null && keyRelated) {\n                sqlSelect += (AS + keyProperty);\n            }\n        } else {\n            sqlSelect = EMPTY;\n        }\n        return sqlSelect;\n    }\n\n    /**\n     * 获取包含主键及字段的 select sql 片段\n     *\n     * @return sql 片段\n     */\n    public String getAllSqlSelect() {\n        if (allSqlSelect != null) {\n            return allSqlSelect;\n        }\n        allSqlSelect = chooseSelect(TableFieldInfo::isSelect);\n        return allSqlSelect;\n    }\n\n    /**\n     * 获取需要进行查询的 select sql 片段\n     *\n     * @param predicate 过滤条件\n     * @return sql 片段\n     */\n    public String chooseSelect(Predicate<TableFieldInfo> predicate) {\n        String sqlSelect = getKeySqlSelect();\n        String fieldsSqlSelect = fieldList.stream().filter(predicate)\n            .map(TableFieldInfo::getSqlSelect).collect(joining(COMMA));\n        if (StringUtils.isNotBlank(sqlSelect) && StringUtils.isNotBlank(fieldsSqlSelect)) {\n            return sqlSelect + COMMA + fieldsSqlSelect;\n        } else if (StringUtils.isNotBlank(fieldsSqlSelect)) {\n            return fieldsSqlSelect;\n        }\n        return sqlSelect;\n    }\n\n    /**\n     * 获取需要进行查询的 select sql 片段\n     *\n     * @param predicate 过滤条件\n     * @return sql 片段\n     */\n    public String chooseSelect(Predicate<TableFieldInfo> predicate, List<String> noSelectProperty) {\n        if (CollectionUtils.isEmpty(noSelectProperty)) {\n            return chooseSelect(predicate);\n        }\n        String fieldsSqlSelect = fieldList.stream().filter(predicate)\n            .filter(i -> !noSelectProperty.contains(i.getProperty()))\n            .map(TableFieldInfo::getSqlSelect).collect(joining(COMMA));\n        if (!havePK() || noSelectProperty.contains(keyProperty)) {\n            return fieldsSqlSelect;\n        } else {\n            return getKeySqlSelect() + COMMA + fieldsSqlSelect;\n        }\n    }\n\n    /**\n     * 获取 insert 时候主键 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"值\" 部位</p>\n     *\n     * @return sql 脚本片段\n     */\n    public String getKeyInsertSqlProperty(final boolean batch, final String prefix, final boolean newLine) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        if (havePK()) {\n            final String prefixKeyProperty = newPrefix + keyProperty;\n            String keyColumn = SqlScriptUtils.safeParam(prefixKeyProperty) + COMMA;\n            if (idType == IdType.AUTO) {\n                if (batch) {\n                    // 批量插入必须返回空自增情况下\n                    return EMPTY;\n                }\n                return SqlScriptUtils.convertIf(keyColumn, String.format(\"%s != null\", prefixKeyProperty), newLine);\n            }\n            return keyColumn + (newLine ? NEWLINE : EMPTY);\n        }\n        return EMPTY;\n    }\n\n    /**\n     * 获取 insert 时候主键 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"字段\" 部位</p>\n     *\n     * @return sql 脚本片段\n     */\n    public String getKeyInsertSqlColumn(final boolean batch, final String prefix, final boolean newLine) {\n        if (havePK()) {\n            final String newPrefix = prefix == null ? EMPTY : prefix;\n            final String prefixKeyProperty = newPrefix + keyProperty;\n            if (idType == IdType.AUTO) {\n                if (batch) {\n                    // 批量插入必须返回空自增情况下\n                    return EMPTY;\n                }\n                return SqlScriptUtils.convertIf(keyColumn + COMMA, String.format(\"%s != null\", prefixKeyProperty), newLine);\n            }\n            return keyColumn + COMMA + (newLine ? NEWLINE : EMPTY);\n        }\n        return EMPTY;\n    }\n\n    /**\n     * 获取所有 insert 时候插入值 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"值\" 部位</p>\n     *\n     * <li> 自动选部位,根据规则会生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getAllInsertSqlPropertyMaybeIf(final String prefix) {\n        return getAllInsertSqlPropertyMaybeIf(prefix, false);\n    }\n\n    /**\n     * 获取所有 insert 时候插入值 sql 脚本片段\n     *\n     * @param prefix                    前缀\n     * @param ignoreAutoIncrementColumn 是否忽略自增主键字段\n     * @return sql 脚本片段\n     * @since 3.5.4\n     */\n    public String getAllInsertSqlPropertyMaybeIf(final String prefix, boolean ignoreAutoIncrementColumn) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        if (ignoreAutoIncrementColumn && idType == IdType.AUTO) {\n            return fieldList.stream()\n                .map(i -> i.getInsertSqlPropertyMaybeIf(newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));\n        }\n        return getKeyInsertSqlProperty(false, newPrefix, true) + fieldList.stream()\n            .map(i -> i.getInsertSqlPropertyMaybeIf(newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));\n    }\n\n    /**\n     * 获取 insert 时候字段 sql 脚本片段\n     * <p>insert into table (字段) values (值)</p>\n     * <p>位于 \"字段\" 部位</p>\n     *\n     * <li> 自动选部位,根据规则会生成 if 标签 </li>\n     *\n     * @return sql 脚本片段\n     */\n    public String getAllInsertSqlColumnMaybeIf(final String prefix) {\n        return getAllInsertSqlColumnMaybeIf(prefix, false);\n    }\n\n    /**\n     * 获取 insert 时候字段 sql 脚本片段\n     *\n     * @param prefix                    前缀\n     * @param ignoreAutoIncrementColumn 是否忽略自增主键字段\n     * @return sql脚本内容\n     * @since 3.5.4\n     */\n    public String getAllInsertSqlColumnMaybeIf(final String prefix, boolean ignoreAutoIncrementColumn) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        if (ignoreAutoIncrementColumn && idType == IdType.AUTO) {\n            return fieldList.stream().map(i -> i.getInsertSqlColumnMaybeIf(newPrefix))\n                .filter(Objects::nonNull).collect(joining(NEWLINE));\n        }\n        return getKeyInsertSqlColumn(false, newPrefix, true) + fieldList.stream().map(i -> i.getInsertSqlColumnMaybeIf(newPrefix))\n            .filter(Objects::nonNull).collect(joining(NEWLINE));\n    }\n\n    /**\n     * 获取所有的查询的 sql 片段\n     *\n     * @param fistAnd             首个条件是否添加 AND 关键字\n     * @param ignoreLogicDelFiled 是否过滤掉逻辑删除字段\n     * @param withId              是否包含 id 项\n     * @param prefix              前缀\n     * @return sql 脚本片段\n     */\n    public String getAllSqlWhere(boolean fistAnd, boolean ignoreLogicDelFiled, boolean withId, final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        String filedSqlScript = fieldList.stream()\n            .filter(i -> {\n                if (ignoreLogicDelFiled) {\n                    return !(isWithLogicDelete() && i.isLogicDelete());\n                }\n                return true;\n            })\n            .map(i -> i.getSqlWhere(newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));\n        if (!withId || StringUtils.isBlank(keyProperty)) {\n            return filedSqlScript;\n        }\n        String newKeyProperty = newPrefix + keyProperty;\n        String keySqlScript = keyColumn + EQUALS + SqlScriptUtils.safeParam(newKeyProperty);\n        return SqlScriptUtils.convertIf(fistAnd ? \" AND \" + keySqlScript : keySqlScript, String.format(\"%s != null\", newKeyProperty), false)\n            + NEWLINE + filedSqlScript;\n    }\n\n    /**\n     * 获取所有的 sql set 片段\n     *\n     * @param ignoreLogicDelFiled 是否过滤掉逻辑删除字段\n     * @param prefix              前缀\n     * @return sql 脚本片段\n     */\n    public String getAllSqlSet(boolean ignoreLogicDelFiled, final String prefix) {\n        final String newPrefix = prefix == null ? EMPTY : prefix;\n        return fieldList.stream()\n            .filter(i -> {\n                if (ignoreLogicDelFiled) {\n                    return !(isWithLogicDelete() && i.isLogicDelete());\n                }\n                return true;\n            }).map(i -> i.getSqlSet(newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));\n    }\n\n    /**\n     * 获取逻辑删除字段的 sql 脚本\n     *\n     * @param startWithAnd 是否以 and 开头\n     * @param isWhere      是否需要的是逻辑删除值\n     * @return sql 脚本\n     */\n    public String getLogicDeleteSql(boolean startWithAnd, boolean isWhere) {\n        if (withLogicDelete) {\n            String logicDeleteSql = formatLogicDeleteSql(isWhere);\n            if (startWithAnd) {\n                logicDeleteSql = \" AND \" + logicDeleteSql;\n            }\n            return logicDeleteSql;\n        }\n        return EMPTY;\n    }\n\n    /**\n     * format logic delete SQL, can be overrided by subclass\n     * github #1386\n     *\n     * @param isWhere true: logicDeleteValue, false: logicNotDeleteValue\n     * @return sql\n     */\n    protected String formatLogicDeleteSql(boolean isWhere) {\n        final String value = isWhere ? logicDeleteFieldInfo.getLogicNotDeleteValue() : logicDeleteFieldInfo.getLogicDeleteValue();\n        if (isWhere) {\n            if (NULL.equalsIgnoreCase(value)) {\n                return logicDeleteFieldInfo.getColumn() + \" IS NULL\";\n            } else {\n                return logicDeleteFieldInfo.getColumn() + EQUALS + String.format(logicDeleteFieldInfo.isCharSequence() ? \"'%s'\" : \"%s\", value);\n            }\n        }\n        final String targetStr = logicDeleteFieldInfo.getColumn() + EQUALS;\n        if (NULL.equalsIgnoreCase(value)) {\n            return targetStr + NULL;\n        } else {\n            return targetStr + String.format(logicDeleteFieldInfo.isCharSequence() ? \"'%s'\" : \"%s\", value);\n        }\n    }\n\n    /**\n     * 自动构建 resultMap 并注入(如果条件符合的话)\n     */\n    void initResultMapIfNeed() {\n        if (autoInitResultMap && null == resultMap) {\n            String id = currentNamespace + DOT + MYBATIS_PLUS + UNDERSCORE + entityType.getSimpleName();\n            List<ResultMapping> resultMappings = new ArrayList<>();\n            if (havePK()) {\n                ResultMapping idMapping = new ResultMapping.Builder(configuration, keyProperty, StringUtils.getTargetColumn(keyColumn), keyType)\n                    .flags(Collections.singletonList(ResultFlag.ID)).build();\n                resultMappings.add(idMapping);\n            }\n            if (CollectionUtils.isNotEmpty(fieldList)) {\n                fieldList.forEach(i -> resultMappings.add(i.getResultMapping(configuration)));\n            }\n            ResultMap resultMap = new ResultMap.Builder(configuration, id, entityType, resultMappings).build();\n            configuration.addResultMap(resultMap);\n            this.resultMap = id;\n        }\n    }\n\n    void setFieldList(List<TableFieldInfo> fieldList) {\n        this.fieldList = fieldList;\n        AtomicInteger logicDeleted = new AtomicInteger();\n        AtomicInteger version = new AtomicInteger();\n        fieldList.forEach(i -> {\n            if (i.isLogicDelete()) {\n                this.withLogicDelete = true;\n                this.logicDeleteFieldInfo = i;\n                logicDeleted.getAndAdd(1);\n            }\n            if (i.isWithInsertFill()) {\n                this.withInsertFill = true;\n            }\n            if (i.isWithUpdateFill()) {\n                this.withUpdateFill = true;\n            }\n            if (i.isVersion()) {\n                this.withVersion = true;\n                this.versionFieldInfo = i;\n                version.getAndAdd(1);\n            }\n        });\n        /* 校验字段合法性 */\n        Assert.isTrue(logicDeleted.get() <= 1, \"@TableLogic not support more than one in Class: \\\"%s\\\"\", entityType.getName());\n        Assert.isTrue(version.get() <= 1, \"@Version not support more than one in Class: \\\"%s\\\"\", entityType.getName());\n    }\n\n    public List<TableFieldInfo> getFieldList() {\n        return Collections.unmodifiableList(fieldList);\n    }\n\n    public List<OrderFieldInfo> getOrderByFields() {\n        if (null == this.orderByFields) {\n            this.orderByFields = new LinkedList<>();\n        }\n        return this.orderByFields;\n    }\n\n    @Deprecated\n    public boolean isLogicDelete() {\n        return withLogicDelete;\n    }\n\n    /**\n     * 获取对象属性值\n     *\n     * @param entity   对象\n     * @param property 属性名\n     * @return 属性值\n     * @since 3.4.4\n     */\n    public Object getPropertyValue(Object entity, String property) {\n        try {\n            return this.reflector.getGetInvoker(property).invoke(entity, null);\n        } catch (ReflectiveOperationException e) {\n            throw ExceptionUtils.mpe(\"Error: Cannot read property in %s.  Cause:\", e, entity.getClass().getSimpleName());\n        }\n    }\n\n    /**\n     * 设置对象属性值\n     *\n     * @param entity   实体对象\n     * @param property 属性名\n     * @param values   参数\n     * @since 3.4.4\n     */\n    public void setPropertyValue(Object entity, String property, Object... values) {\n        try {\n            this.reflector.getSetInvoker(property).invoke(entity, values);\n        } catch (ReflectiveOperationException e) {\n            throw ExceptionUtils.mpe(\"Error: Cannot write property in %s.  Cause:\", e, entity.getClass().getSimpleName());\n        }\n    }\n\n    /**\n     * 创建实例\n     *\n     * @param <T> 泛型\n     * @return 初始化实例\n     * @since 3.5.0\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T newInstance() {\n        return (T) configuration.getObjectFactory().create(entityType);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/TableInfoHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.metadata;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.builder.StaticSqlSource;\nimport org.apache.ibatis.builder.annotation.ProviderContext;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.SelectKeyGenerator;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.parsing.PropertyParser;\nimport org.apache.ibatis.reflection.Reflector;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.type.SimpleTypeRegistry;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\n\nimport static java.util.stream.Collectors.toList;\n\n/**\n * <p>\n * 实体类反射表辅助类\n * </p>\n *\n * @author hubin sjy\n * @since 2016-09-09\n */\npublic class TableInfoHelper {\n\n    private static final Log logger = LogFactory.getLog(TableInfoHelper.class);\n\n    /**\n     * 储存反射类表信息\n     */\n    private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 储存表名对应的反射类表信息\n     */\n    private static final Map<String, TableInfo> TABLE_NAME_INFO_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 默认表主键名称\n     */\n    private static final String DEFAULT_ID_NAME = \"id\";\n\n    /**\n     * <p>\n     * 获取实体映射表信息\n     * </p>\n     *\n     * @param clazz 反射实体类\n     * @return 数据库表反射信息\n     */\n    public static TableInfo getTableInfo(Class<?> clazz) {\n        if (clazz == null || clazz.isPrimitive() || SimpleTypeRegistry.isSimpleType(clazz) || clazz.isInterface()) {\n            return null;\n        }\n        // https://github.com/baomidou/mybatis-plus/issues/299\n        Class<?> targetClass = ClassUtils.getUserClass(clazz);\n        TableInfo tableInfo = TABLE_INFO_CACHE.get(targetClass);\n        if (null != tableInfo) {\n            return tableInfo;\n        }\n        //尝试获取父类缓存\n        Class<?> currentClass = clazz;\n        while (null == tableInfo && Object.class != currentClass) {\n            currentClass = currentClass.getSuperclass();\n            tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(currentClass));\n        }\n        //把父类的移到子类中来\n        if (tableInfo != null) {\n            TABLE_INFO_CACHE.put(targetClass, tableInfo);\n        }\n        return tableInfo;\n    }\n\n    /**\n     * <p>\n     * SelectProvider 方式上下文中获取 SQL 脚本\n     * </p>\n     *\n     * @param context {@link ProviderContext}\n     * @param sqlFunc 回调执行 SQL 函数\n     * @return SQL 脚本\n     */\n    public static String getSqlScript(ProviderContext context, Function<TableInfo, String> sqlFunc) {\n        final Class<?> mapperClass = context.getMapperType();\n        Class<?> entityClass = ReflectionKit.getSuperClassGenericType(mapperClass, BaseMapper.class, 0);\n        return sqlFunc.apply(getTableInfo(entityClass));\n    }\n\n    /**\n     * <p>\n     * 根据表名获取实体映射表信息\n     * </p>\n     *\n     * @param tableName 表名\n     * @return 数据库表反射信息\n     */\n    public static TableInfo getTableInfo(String tableName) {\n        if (StringUtils.isBlank(tableName)) {\n            return null;\n        }\n        return TABLE_NAME_INFO_CACHE.get(tableName);\n    }\n\n    /**\n     * <p>\n     * 获取所有实体映射表信息\n     * </p>\n     *\n     * @return 数据库表反射信息集合\n     */\n    public static List<TableInfo> getTableInfos() {\n        return Collections.unmodifiableList(new ArrayList<>(TABLE_INFO_CACHE.values()));\n    }\n\n    /**\n     * 清空实体表映射缓存信息\n     *\n     * @param entityClass 实体 Class\n     */\n    public static void remove(Class<?> entityClass) {\n        TABLE_INFO_CACHE.remove(entityClass);\n    }\n\n    /**\n     * <p>\n     * 实体类反射获取表信息【初始化】\n     * </p>\n     *\n     * @param clazz 反射实体类\n     * @return 数据库表反射信息\n     */\n    public static synchronized TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {\n        TableInfo targetTableInfo = TABLE_INFO_CACHE.get(clazz);\n        final Configuration configuration = builderAssistant.getConfiguration();\n        if (targetTableInfo != null) {\n            Configuration oldConfiguration = targetTableInfo.getConfiguration();\n            if (!oldConfiguration.equals(configuration)) {\n                // 不是同一个 Configuration,进行重新初始化\n                targetTableInfo = initTableInfo(configuration, builderAssistant.getCurrentNamespace(), clazz);\n            }\n            return targetTableInfo;\n        }\n        return initTableInfo(configuration, builderAssistant.getCurrentNamespace(), clazz);\n    }\n\n    /**\n     * <p>\n     * 实体类反射获取表信息【初始化】\n     * </p>\n     *\n     * @param clazz 反射实体类\n     * @return 数据库表反射信息\n     */\n    private static synchronized TableInfo initTableInfo(Configuration configuration, String currentNamespace, Class<?> clazz) {\n        GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);\n        PostInitTableInfoHandler postInitTableInfoHandler = globalConfig.getPostInitTableInfoHandler();\n        /* 没有获取到缓存信息,则初始化 */\n        TableInfo tableInfo = postInitTableInfoHandler.creteTableInfo(configuration, clazz);\n        tableInfo.setCurrentNamespace(currentNamespace);\n\n        /* 初始化表名相关 */\n        PropertySelector propertySelector = initTableName(clazz, globalConfig, tableInfo);\n\n        /* 初始化字段相关 */\n        initTableFields(configuration, clazz, globalConfig, tableInfo, propertySelector);\n\n        /* 自动构建 resultMap */\n        tableInfo.initResultMapIfNeed();\n        postInitTableInfoHandler.postTableInfo(tableInfo, configuration);\n        TABLE_INFO_CACHE.put(clazz, tableInfo);\n        TABLE_NAME_INFO_CACHE.put(tableInfo.getTableName(), tableInfo);\n\n        /* 缓存 lambda */\n        LambdaUtils.installCache(tableInfo);\n        return tableInfo;\n    }\n\n    /**\n     * <p>\n     * 初始化 表数据库类型,表名,resultMap\n     * </p>\n     *\n     * @param clazz        实体类\n     * @param globalConfig 全局配置\n     * @param tableInfo    数据库表反射信息\n     * @return 需要排除的字段名\n     */\n    private static PropertySelector initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {\n        /* 数据库全局配置 */\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler();\n        TableName table = annotationHandler.getAnnotation(clazz, TableName.class);\n        Configuration configuration = tableInfo.getConfiguration();\n        String tableName = clazz.getSimpleName();\n        String tablePrefix = dbConfig.getTablePrefix();\n        String schema = dbConfig.getSchema();\n        boolean tablePrefixEffect = true;\n        PropertySelector propertySelector = i -> true;\n        if (table != null) {\n            if (StringUtils.isNotBlank(table.value())) {\n                tableName = PropertyParser.parse(table.value(), configuration.getVariables());\n                if (StringUtils.isNotBlank(tablePrefix) && !table.keepGlobalPrefix()) {\n                    tablePrefixEffect = false;\n                }\n            } else {\n                tableName = initTableNameWithDbConfig(tableName, dbConfig);\n            }\n            if (StringUtils.isNotBlank(table.schema())) {\n                schema = PropertyParser.parse(table.schema(), configuration.getVariables());\n            }\n            /* 表结果集映射 */\n            if (StringUtils.isNotBlank(table.resultMap())) {\n                tableInfo.setResultMap(table.resultMap());\n            }\n            tableInfo.setAutoInitResultMap(table.autoResultMap());\n            String[] ep = table.excludeProperty();\n            String[] ip = table.properties();\n            if (ArrayUtils.isNotEmpty(ip)) {\n                List<String> list = Arrays.asList(ip);\n                propertySelector = list::contains;\n            } else if (ArrayUtils.isNotEmpty(ep)) {\n                List<String> list = Arrays.asList(ep);\n                propertySelector = i -> !list.contains(i);\n            }\n        } else {\n            tableName = initTableNameWithDbConfig(tableName, dbConfig);\n        }\n\n        // 表追加前缀\n        String targetTableName = tableName;\n        if (StringUtils.isNotBlank(tablePrefix) && tablePrefixEffect) {\n            targetTableName = tablePrefix + targetTableName;\n        }\n\n        // 表格式化\n        String tableFormat = dbConfig.getTableFormat();\n        if (StringUtils.isNotBlank(tableFormat)) {\n            targetTableName = String.format(tableFormat, targetTableName);\n        }\n\n        // 表追加 schema 信息\n        if (StringUtils.isNotBlank(schema)) {\n            targetTableName = schema + StringPool.DOT + targetTableName;\n        }\n\n        tableInfo.setTableName(targetTableName);\n\n        /* 开启了自定义 KEY 生成器 */\n        if (CollectionUtils.isNotEmpty(dbConfig.getKeyGenerators())) {\n            tableInfo.setKeySequence(annotationHandler.getAnnotation(clazz, KeySequence.class));\n        }\n        return propertySelector;\n    }\n\n    /**\n     * 根据 DbConfig 初始化 表名\n     *\n     * @param className 类名\n     * @param dbConfig  DbConfig\n     * @return 表名\n     */\n    private static String initTableNameWithDbConfig(String className, GlobalConfig.DbConfig dbConfig) {\n        String tableName = className;\n        // 开启表名下划线申明\n        if (dbConfig.isTableUnderline()) {\n            tableName = StringUtils.camelToUnderline(tableName);\n        }\n        // 大写命名判断\n        if (dbConfig.isCapitalMode()) {\n            tableName = tableName.toUpperCase();\n        } else {\n            // 首字母小写\n            tableName = StringUtils.firstToLowerCase(tableName);\n        }\n        return tableName;\n    }\n\n    /**\n     * <p>\n     * 初始化 表主键,表字段\n     * </p>\n     *\n     * @param clazz        实体类\n     * @param globalConfig 全局配置\n     * @param tableInfo    数据库表反射信息\n     */\n    private static void initTableFields(Configuration configuration, Class<?> clazz, GlobalConfig globalConfig,\n                                        TableInfo tableInfo, PropertySelector propertySelector) {\n        AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler();\n        PostInitTableInfoHandler postInitTableInfoHandler = globalConfig.getPostInitTableInfoHandler();\n        Reflector reflector = tableInfo.getReflector();\n        List<Field> list = getAllFields(clazz, annotationHandler);\n        // 标记是否读取到主键\n        boolean isReadPK = false;\n        // 是否存在 @TableId 注解\n        boolean existTableId = isExistTableId(list, annotationHandler);\n        // 是否存在 @TableLogic 注解\n        boolean existTableLogic = isExistTableLogic(list, annotationHandler);\n\n        List<TableFieldInfo> fieldList = new ArrayList<>(list.size());\n        for (Field field : list) {\n            if (!propertySelector.selection(field.getName())) {\n                continue;\n            }\n\n            boolean isPK = false;\n            OrderBy orderBy = annotationHandler.getAnnotation(field, OrderBy.class);\n            boolean isOrderBy = orderBy != null;\n            /* 主键ID 初始化 */\n            if (existTableId) {\n                TableId tableId = annotationHandler.getAnnotation(field, TableId.class);\n                if (tableId != null) {\n                    if (isReadPK) {\n                        throw ExceptionUtils.mpe(\"@TableId can't more than one in Class: \\\"%s\\\".\", clazz.getName());\n                    }\n\n                    initTableIdWithAnnotation(globalConfig, tableInfo, field, tableId);\n                    isPK = isReadPK = true;\n                }\n            } else if (!isReadPK) {\n                isPK = isReadPK = initTableIdWithoutAnnotation(globalConfig, tableInfo, field);\n            }\n\n            if (isPK) {\n                if (orderBy != null) {\n                    tableInfo.getOrderByFields().add(new OrderFieldInfo(tableInfo.getKeyColumn(), orderBy.asc(), orderBy.sort()));\n                }\n                continue;\n            }\n            final TableField tableField = annotationHandler.getAnnotation(field, TableField.class);\n\n            /* 有 @TableField 注解的字段初始化 */\n            if (tableField != null) {\n                TableFieldInfo tableFieldInfo = new TableFieldInfo(globalConfig, tableInfo, field, tableField, reflector, existTableLogic, isOrderBy);\n                fieldList.add(tableFieldInfo);\n                postInitTableInfoHandler.postFieldInfo(tableFieldInfo, configuration);\n                continue;\n            }\n\n            /* 无 @TableField  注解的字段初始化 */\n            TableFieldInfo tableFieldInfo = new TableFieldInfo(globalConfig, tableInfo, field, reflector, existTableLogic, isOrderBy);\n            fieldList.add(tableFieldInfo);\n            postInitTableInfoHandler.postFieldInfo(tableFieldInfo, configuration);\n        }\n\n        /* 字段列表 */\n        tableInfo.setFieldList(fieldList);\n\n        /* 未发现主键注解，提示警告信息 */\n        if (!isReadPK) {\n            logger.warn(String.format(\"Can not find table primary key in Class: \\\"%s\\\".\", clazz.getName()));\n        }\n    }\n\n    /**\n     * <p>\n     * 判断主键注解是否存在\n     * </p>\n     *\n     * @param clazz 实体类\n     * @param list  字段列表\n     * @return true 为存在 {@link TableId} 注解;\n     */\n    public static boolean isExistTableId(Class<?> clazz, List<Field> list) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(tableInfo.getConfiguration()).getAnnotationHandler();\n        return isExistTableId(list, annotationHandler);\n    }\n\n    /**\n     * <p>\n     * 判断主键注解是否存在\n     * </p>\n     *\n     * @param list 字段列表\n     * @return true 为存在 {@link TableId} 注解;\n     */\n    public static boolean isExistTableId(List<Field> list, AnnotationHandler annotationHandler) {\n        return list.stream().anyMatch(field -> annotationHandler.isAnnotationPresent(field, TableId.class));\n    }\n\n    /**\n     * <p>\n     * 判断逻辑删除注解是否存在\n     * </p>\n     *\n     * @param clazz 实体类\n     * @param list  字段列表\n     * @return true 为存在 {@link TableLogic} 注解;\n     */\n    public static boolean isExistTableLogic(Class<?> clazz, List<Field> list) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(tableInfo.getConfiguration()).getAnnotationHandler();\n        return isExistTableLogic(list, annotationHandler);\n    }\n\n    /**\n     * <p>\n     * 判断逻辑删除注解是否存在\n     * </p>\n     *\n     * @param list 字段列表\n     * @return true 为存在 {@link TableLogic} 注解;\n     */\n    public static boolean isExistTableLogic(List<Field> list, AnnotationHandler annotationHandler) {\n        return list.stream().anyMatch(field -> annotationHandler.isAnnotationPresent(field, TableLogic.class));\n    }\n\n    /**\n     * <p>\n     * 判断排序注解是否存在\n     * </p>\n     *\n     * @param clazz 实体类\n     * @param list  字段列表\n     * @return true 为存在 {@link OrderBy} 注解;\n     */\n    public static boolean isExistOrderBy(Class<?> clazz, List<Field> list) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(tableInfo.getConfiguration()).getAnnotationHandler();\n        return isExistOrderBy(list, annotationHandler);\n    }\n\n    /**\n     * <p>\n     * 判断排序注解是否存在\n     * </p>\n     *\n     * @param list              字段列表\n     * @param annotationHandler 注解处理类\n     * @return true 为存在 {@link OrderBy} 注解;\n     */\n    public static boolean isExistOrderBy(List<Field> list, AnnotationHandler annotationHandler) {\n        return list.stream().anyMatch(field -> annotationHandler.isAnnotationPresent(field, OrderBy.class));\n    }\n\n    /**\n     * <p>\n     * 主键属性初始化\n     * </p>\n     *\n     * @param globalConfig 全局配置信息\n     * @param tableInfo    表信息\n     * @param field        字段\n     * @param tableId      注解\n     */\n    private static void initTableIdWithAnnotation(GlobalConfig globalConfig, TableInfo tableInfo, Field field, TableId tableId) {\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        boolean underCamel = tableInfo.isUnderCamel();\n        final String property = field.getName();\n        if (globalConfig.getAnnotationHandler().isAnnotationPresent(field, TableField.class)) {\n            logger.warn(String.format(\"This \\\"%s\\\" is the table primary key by @TableId annotation in Class: \\\"%s\\\",So @TableField annotation will not work!\",\n                property, tableInfo.getEntityType().getName()));\n        }\n        /* 主键策略（ 注解 > 全局 ） */\n        // 设置 Sequence 其他策略无效\n        if (IdType.NONE == tableId.type()) {\n            tableInfo.setIdType(dbConfig.getIdType());\n        } else {\n            tableInfo.setIdType(tableId.type());\n        }\n\n        /* 字段 */\n        String column = property;\n        if (StringUtils.isNotBlank(tableId.value())) {\n            column = tableId.value();\n        } else {\n            // 开启字段下划线申明\n            if (underCamel) {\n                column = StringUtils.camelToUnderline(column);\n            }\n            // 全局大写命名\n            if (dbConfig.isCapitalMode()) {\n                column = column.toUpperCase();\n            }\n        }\n        final Class<?> keyType = tableInfo.getReflector().getGetterType(property);\n        if (keyType.isPrimitive()) {\n            logger.warn(String.format(\"This primary key of \\\"%s\\\" is primitive !不建议如此请使用包装类 in Class: \\\"%s\\\"\",\n                property, tableInfo.getEntityType().getName()));\n        }\n        if (StringUtils.isEmpty(tableId.value())) {\n            String columnFormat = dbConfig.getColumnFormat();\n            if (StringUtils.isNotBlank(columnFormat)) {\n                column = String.format(columnFormat, column);\n            }\n        }\n        tableInfo.setKeyRelated(checkRelated(underCamel, property, column))\n            .setKeyColumn(column)\n            .setKeyProperty(property)\n            .setKeyType(keyType);\n    }\n\n    /**\n     * <p>\n     * 主键属性初始化\n     * </p>\n     *\n     * @param globalConfig 全局配置\n     * @param tableInfo    表信息\n     * @param field        字段\n     * @return true 继续下一个属性判断，返回 continue;\n     */\n    private static boolean initTableIdWithoutAnnotation(GlobalConfig globalConfig, TableInfo tableInfo, Field field) {\n        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();\n        final String property = field.getName();\n        if (DEFAULT_ID_NAME.equalsIgnoreCase(property)) {\n            if (globalConfig.getAnnotationHandler().isAnnotationPresent(field, TableField.class)) {\n                logger.warn(String.format(\"This \\\"%s\\\" is the table primary key by default name for `id` in Class: \\\"%s\\\",So @TableField will not work!\",\n                    property, tableInfo.getEntityType().getName()));\n            }\n            String column = property;\n            if (dbConfig.isCapitalMode()) {\n                column = column.toUpperCase();\n            }\n            final Class<?> keyType = tableInfo.getReflector().getGetterType(property);\n            if (keyType.isPrimitive()) {\n                logger.warn(String.format(\"This primary key of \\\"%s\\\" is primitive !不建议如此请使用包装类 in Class: \\\"%s\\\"\",\n                    property, tableInfo.getEntityType().getName()));\n            }\n            String columnFormat = dbConfig.getColumnFormat();\n            if (StringUtils.isNotBlank(columnFormat)) {\n                column = String.format(columnFormat, column);\n            }\n            tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column))\n                .setIdType(dbConfig.getIdType())\n                .setKeyColumn(column)\n                .setKeyProperty(property)\n                .setKeyType(keyType);\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 判定 related 的值\n     * <p>\n     * 为 true 表示不符合规则\n     *\n     * @param underCamel 驼峰命名\n     * @param property   属性名\n     * @param column     字段名\n     * @return related\n     */\n    public static boolean checkRelated(boolean underCamel, String property, String column) {\n        column = StringUtils.getTargetColumn(column);\n        String propertyUpper = property.toUpperCase(Locale.ENGLISH);\n        String columnUpper = column.toUpperCase(Locale.ENGLISH);\n        if (underCamel) {\n            // 开启了驼峰并且 column 包含下划线\n            return !(propertyUpper.equals(columnUpper) ||\n                propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));\n        } else {\n            // 未开启驼峰,直接判断 property 是否与 column 相同(全大写)\n            return !propertyUpper.equals(columnUpper);\n        }\n    }\n\n    /**\n     * <p>\n     * 获取该类的所有属性列表\n     * </p>\n     *\n     * @param clazz 反射类\n     * @return 属性集合\n     */\n    public static List<Field> getAllFields(Class<?> clazz) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        AnnotationHandler annotationHandler = GlobalConfigUtils.getGlobalConfig(tableInfo.getConfiguration()).getAnnotationHandler();\n        return getAllFields(clazz, annotationHandler);\n    }\n\n    /**\n     * <p>\n     * 获取该类的所有属性列表\n     * </p>\n     *\n     * @param clazz             反射类\n     * @param annotationHandler 注解处理类\n     * @return 属性集合\n     */\n    public static List<Field> getAllFields(Class<?> clazz, AnnotationHandler annotationHandler) {\n        List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));\n        return fieldList.stream()\n            .filter(field -> {\n                /* 过滤注解非表字段属性 */\n                TableField tableField = annotationHandler.getAnnotation(field, TableField.class);\n                return (tableField == null || tableField.exist());\n            }).collect(toList());\n    }\n\n    public static KeyGenerator genKeyGenerator(String baseStatementId, TableInfo tableInfo, MapperBuilderAssistant builderAssistant) {\n        List<IKeyGenerator> keyGenerators = GlobalConfigUtils.getKeyGenerators(builderAssistant.getConfiguration());\n        if (CollectionUtils.isEmpty(keyGenerators)) {\n            throw new IllegalArgumentException(\"not configure IKeyGenerator implementation class.\");\n        }\n        IKeyGenerator keyGenerator = null;\n        if (keyGenerators.size() > 1) {\n            // 多个主键生成器\n            KeySequence keySequence = tableInfo.getKeySequence();\n            if (null != keySequence && DbType.OTHER != keySequence.dbType()) {\n                keyGenerator = keyGenerators.stream().filter(k -> k.dbType() == keySequence.dbType()).findFirst().orElse(null);\n            }\n        }\n        // 无法找到注解指定生成器，默认使用第一个生成器\n        if (null == keyGenerator) {\n            keyGenerator = keyGenerators.get(0);\n        }\n        Configuration configuration = builderAssistant.getConfiguration();\n        String id = builderAssistant.getCurrentNamespace() + StringPool.DOT + baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;\n        ResultMap resultMap = new ResultMap.Builder(builderAssistant.getConfiguration(), id, tableInfo.getKeyType(), new ArrayList<>()).build();\n        MappedStatement mappedStatement = new MappedStatement.Builder(builderAssistant.getConfiguration(), id,\n            new StaticSqlSource(configuration, keyGenerator.executeSql(tableInfo.getKeySequence().value())), SqlCommandType.SELECT)\n            .keyProperty(tableInfo.getKeyProperty())\n            .resultMaps(Collections.singletonList(resultMap))\n            .build();\n        configuration.addMappedStatement(mappedStatement);\n        return new SelectKeyGenerator(mappedStatement, true);\n    }\n\n    @FunctionalInterface\n    private interface PropertySelector {\n        boolean selection(String property);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/metadata/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 反射表结构元数据\n */\npackage com.baomidou.mybatisplus.core.metadata;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.override;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport org.apache.ibatis.binding.BindingException;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.StatementType;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.RowBounds;\nimport org.apache.ibatis.session.SqlSession;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * 从  {@link MapperMethod} copy 过来 </br>\n * <p> 不要内部类 ParamMap </p>\n * <p> 不要内部类 SqlCommand </p>\n * <p> 不要内部类 MethodSignature </p>\n *\n * @author miemie\n * @since 2018-06-09\n */\npublic class MybatisMapperMethod {\n    private final MapperMethod.SqlCommand command;\n    private final MapperMethod.MethodSignature method;\n\n    public MybatisMapperMethod(Class<?> mapperInterface, Method method, Configuration config) {\n        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);\n        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);\n    }\n\n    public Object execute(SqlSession sqlSession, Object[] args) {\n        Object result;\n        switch (command.getType()) {\n            case INSERT: {\n                Object param = method.convertArgsToSqlCommandParam(args);\n                result = rowCountResult(sqlSession.insert(command.getName(), param));\n                break;\n            }\n            case UPDATE: {\n                Object param = method.convertArgsToSqlCommandParam(args);\n                result = rowCountResult(sqlSession.update(command.getName(), param));\n                break;\n            }\n            case DELETE: {\n                Object param = method.convertArgsToSqlCommandParam(args);\n                result = rowCountResult(sqlSession.delete(command.getName(), param));\n                break;\n            }\n            case SELECT:\n                if (method.returnsVoid() && method.hasResultHandler()) {\n                    executeWithResultHandler(sqlSession, args);\n                    result = null;\n                } else if (method.returnsMany()) {\n                    result = executeForMany(sqlSession, args);\n                } else if (method.returnsMap()) {\n                    result = executeForMap(sqlSession, args);\n                } else if (method.returnsCursor()) {\n                    result = executeForCursor(sqlSession, args);\n                } else {\n                    if (IPage.class.isAssignableFrom(method.getReturnType())) {\n                        result = executeForIPage(sqlSession, args);\n                    } else {\n                        Object param = method.convertArgsToSqlCommandParam(args);\n                        result = sqlSession.selectOne(command.getName(), param);\n                        if (method.returnsOptional()\n                            && (result == null || !method.getReturnType().equals(result.getClass()))) {\n                            result = Optional.ofNullable(result);\n                        }\n                    }\n                }\n                break;\n            case FLUSH:\n                result = sqlSession.flushStatements();\n                break;\n            default:\n                throw new BindingException(\"Unknown execution method for: \" + command.getName());\n        }\n        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {\n            throw new BindingException(\"Mapper method '\" + command.getName()\n                + \" attempted to return null from a method with a primitive return type (\" + method.getReturnType() + \").\");\n        }\n        return result;\n    }\n\n    @SuppressWarnings(\"all\")\n    private <E> Object executeForIPage(SqlSession sqlSession, Object[] args) {\n        IPage<E> result = null;\n        for (Object arg : args) {\n            if (arg instanceof IPage) {\n                result = (IPage<E>) arg;\n                break;\n            }\n        }\n        Assert.notNull(result, \"can't found IPage for args!\");\n        Object param = method.convertArgsToSqlCommandParam(args);\n        List<E> list = sqlSession.selectList(command.getName(), param);\n        result.setRecords(list);\n        return result;\n    }\n\n    private Object rowCountResult(int rowCount) {\n        final Object result;\n        if (method.returnsVoid()) {\n            result = null;\n        } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {\n            result = rowCount;\n        } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {\n            result = (long) rowCount;\n        } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {\n            result = rowCount > 0;\n        } else {\n            throw new BindingException(\"Mapper method '\" + command.getName() + \"' has an unsupported return type: \" + method.getReturnType());\n        }\n        return result;\n    }\n\n    private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {\n        MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());\n        if (!StatementType.CALLABLE.equals(ms.getStatementType())\n            && void.class.equals(ms.getResultMaps().get(0).getType())) {\n            throw new BindingException(\"method \" + command.getName()\n                + \" needs either a @ResultMap annotation, a @ResultType annotation,\"\n                + \" or a resultType attribute in XML so a ResultHandler can be used as a parameter.\");\n        }\n        Object param = method.convertArgsToSqlCommandParam(args);\n        if (method.hasRowBounds()) {\n            RowBounds rowBounds = method.extractRowBounds(args);\n            sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));\n        } else {\n            sqlSession.select(command.getName(), param, method.extractResultHandler(args));\n        }\n    }\n\n    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {\n        List<E> result;\n        Object param = method.convertArgsToSqlCommandParam(args);\n        if (method.hasRowBounds()) {\n            RowBounds rowBounds = method.extractRowBounds(args);\n            result = sqlSession.selectList(command.getName(), param, rowBounds);\n        } else {\n            result = sqlSession.selectList(command.getName(), param);\n        }\n        // issue #510 Collections & arrays support\n        if (!method.getReturnType().isAssignableFrom(result.getClass())) {\n            if (method.getReturnType().isArray()) {\n                return convertToArray(result);\n            } else {\n                return convertToDeclaredCollection(sqlSession.getConfiguration(), result);\n            }\n        }\n        return result;\n    }\n\n    private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {\n        Cursor<T> result;\n        Object param = method.convertArgsToSqlCommandParam(args);\n        if (method.hasRowBounds()) {\n            RowBounds rowBounds = method.extractRowBounds(args);\n            result = sqlSession.selectCursor(command.getName(), param, rowBounds);\n        } else {\n            result = sqlSession.selectCursor(command.getName(), param);\n        }\n        return result;\n    }\n\n    private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {\n        Object collection = config.getObjectFactory().create(method.getReturnType());\n        MetaObject metaObject = config.newMetaObject(collection);\n        metaObject.addAll(list);\n        return collection;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <E> Object convertToArray(List<E> list) {\n        Class<?> arrayComponentType = method.getReturnType().getComponentType();\n        Object array = Array.newInstance(arrayComponentType, list.size());\n        if (!arrayComponentType.isPrimitive()) {\n            return list.toArray((E[]) array);\n        }\n        for (int i = 0; i < list.size(); i++) {\n            Array.set(array, i, list.get(i));\n        }\n        return array;\n    }\n\n    private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {\n        Map<K, V> result;\n        Object param = method.convertArgsToSqlCommandParam(args);\n        if (method.hasRowBounds()) {\n            RowBounds rowBounds = method.extractRowBounds(args);\n            result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);\n        } else {\n            result = sqlSession.selectMap(command.getName(), param, method.getMapKey());\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxy.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.override;\n\nimport com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata;\nimport com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisUtils;\nimport org.apache.ibatis.binding.MapperProxy;\nimport org.apache.ibatis.reflection.ExceptionUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.util.MapUtil;\n\nimport java.io.Serializable;\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.Map;\n\n/**\n * <p> 从 {@link MapperProxy}  copy 过来 </p>\n * <li> 使用 MybatisMapperMethod </li>\n *\n * @author miemie\n * @since 2018-06-09\n */\npublic class MybatisMapperProxy<T> implements InvocationHandler, Serializable {\n\n    private static final long serialVersionUID = -4724728412955527868L;\n    private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED\n        | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;\n    private static final Constructor<MethodHandles.Lookup> lookupConstructor;\n    private static final Method privateLookupInMethod;\n    private final SqlSession sqlSession;\n    private final Class<T> mapperInterface;\n    private final Map<Method, MapperMethodInvoker> methodCache;\n\n    public MybatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {\n        this.sqlSession = sqlSession;\n        this.mapperInterface = mapperInterface;\n        this.methodCache = methodCache;\n    }\n\n    static {\n        Method privateLookupIn;\n        try {\n            privateLookupIn = MethodHandles.class.getMethod(\"privateLookupIn\", Class.class, MethodHandles.Lookup.class);\n        } catch (NoSuchMethodException e) {\n            privateLookupIn = null;\n        }\n        privateLookupInMethod = privateLookupIn;\n\n        Constructor<MethodHandles.Lookup> lookup = null;\n        if (privateLookupInMethod == null) {\n            // JDK 1.8\n            try {\n                lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);\n                lookup.setAccessible(true);\n            } catch (NoSuchMethodException e) {\n                throw new IllegalStateException(\n                    \"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.\",\n                    e);\n            } catch (Exception e) {\n                lookup = null;\n            }\n        }\n        lookupConstructor = lookup;\n    }\n\n    @Override\n    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n        try {\n            if (Object.class.equals(method.getDeclaringClass())) {\n                return method.invoke(this, args);\n            }\n            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);\n        } catch (Throwable t) {\n            throw ExceptionUtil.unwrapThrowable(t);\n        }\n    }\n\n    private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {\n        try {\n            return MapUtil.computeIfAbsent(methodCache, method, m -> {\n                if (!m.isDefault()) {\n                    return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));\n                }\n                try {\n                    if (privateLookupInMethod == null) {\n                        return new DefaultMethodInvoker(getMethodHandleJava8(method));\n                    }\n                    return new DefaultMethodInvoker(getMethodHandleJava9(method));\n                } catch (IllegalAccessException | InstantiationException | InvocationTargetException\n                         | NoSuchMethodException e) {\n                    throw new RuntimeException(e);\n                }\n            });\n        } catch (RuntimeException re) {\n            Throwable cause = re.getCause();\n            throw cause == null ? re : cause;\n        }\n    }\n\n    public SqlSession getSqlSession() {\n        return sqlSession;\n    }\n\n    public Class<T> getMapperInterface() {\n        return mapperInterface;\n    }\n\n    private MethodHandle getMethodHandleJava9(Method method)\n        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {\n        final Class<?> declaringClass = method.getDeclaringClass();\n        return ((MethodHandles.Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(\n            declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),\n            declaringClass);\n    }\n\n    private MethodHandle getMethodHandleJava8(Method method)\n        throws IllegalAccessException, InstantiationException, InvocationTargetException {\n        final Class<?> declaringClass = method.getDeclaringClass();\n        return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);\n    }\n\n    interface MapperMethodInvoker {\n        Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;\n    }\n\n    private static class PlainMethodInvoker implements MapperMethodInvoker {\n        private final MybatisMapperMethod mapperMethod;\n\n        public PlainMethodInvoker(MybatisMapperMethod mapperMethod) {\n            this.mapperMethod = mapperMethod;\n        }\n\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {\n            return mapperMethod.execute(sqlSession, args);\n        }\n    }\n\n    private static class DefaultMethodInvoker implements MapperMethodInvoker {\n        private final MethodHandle methodHandle;\n\n        public DefaultMethodInvoker(MethodHandle methodHandle) {\n            super();\n            this.methodHandle = methodHandle;\n        }\n\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {\n            boolean hasIgnoreStrategy = InterceptorIgnoreHelper.hasIgnoreStrategy();\n            if (hasIgnoreStrategy) {\n                return methodHandle.bindTo(proxy).invokeWithArguments(args);\n            } else {\n                try {\n                    MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(proxy);\n                    Class<?> mapperInterface = mapperProxyMetadata.getMapperInterface();\n                    IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.findIgnoreStrategy(mapperInterface, method);\n                    if (ignoreStrategy == null) {\n                        ignoreStrategy = IgnoreStrategy.builder().build();\n                    }\n                    InterceptorIgnoreHelper.handle(ignoreStrategy);\n                    return methodHandle.bindTo(proxy).invokeWithArguments(args);\n                } finally {\n                    InterceptorIgnoreHelper.clearIgnoreStrategy();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperProxyFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.override;\n\nimport lombok.Getter;\nimport org.apache.ibatis.binding.MapperProxyFactory;\nimport org.apache.ibatis.session.SqlSession;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * <p>从 {@link MapperProxyFactory} copy 过来 </p>\n * <li> 使用 MybatisMapperMethod </li>\n *\n * @author miemie\n * @since 2018-06-09\n */\npublic class MybatisMapperProxyFactory<T> {\n\n    @Getter\n    private final Class<T> mapperInterface;\n    @Getter\n    private final Map<Method, MybatisMapperProxy.MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();\n\n    public MybatisMapperProxyFactory(Class<T> mapperInterface) {\n        this.mapperInterface = mapperInterface;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected T newInstance(MybatisMapperProxy<T> mapperProxy) {\n        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);\n    }\n\n    public T newInstance(SqlSession sqlSession) {\n        final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);\n        return newInstance(mapperProxy);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Page 部分重构相关逻辑类\n *\n * @author hubin\n * @since 2018-06-09\n */\npackage com.baomidou.mybatisplus.core.override;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 只放mybatis-plus核心代码\n *\n * @author yuxiaobin\n * @since 2018-02-07\n */\npackage com.baomidou.mybatisplus.core;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/IgnoreStrategy.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.plugins;\n\nimport lombok.Builder;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.Map;\n\n@Getter\n@Setter\n@Builder\npublic class IgnoreStrategy {\n    private Boolean tenantLine;\n    private Boolean dynamicTableName;\n    private Boolean blockAttack;\n    private Boolean illegalSql;\n    private Boolean dataPermission;\n    private Map<String, Boolean> others;\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.plugins;\n\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport org.apache.ibatis.executor.keygen.SelectKeyGenerator;\n\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\n/**\n * @author miemie\n * @since 2020-07-31\n */\npublic abstract class InterceptorIgnoreHelper {\n\n    /**\n     * SQL 解析缓存\n     * key 可能是 mappedStatement 的 ID,也可能是 class 的 name\n     */\n    private static final Map<String, IgnoreStrategy> IGNORE_STRATEGY_CACHE = new ConcurrentHashMap<>();\n    /**\n     *  本地线程拦截器忽略策略缓存\n     */\n    private static final ThreadLocal<IgnoreStrategy> IGNORE_STRATEGY_LOCAL = new ThreadLocal<>();\n\n    /**\n     * 手动设置拦截器忽略执行策略，权限大于注解权限\n     * <p>\n     * InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());\n     * </p>\n     * <p>\n     * 注意，需要手动关闭调用方法 {@link #clearIgnoreStrategy()}\n     * </p>\n     * <p>简化操作可请使用{@link #execute(IgnoreStrategy, Supplier)}</p>\n     *\n     * @param ignoreStrategy {@link IgnoreStrategy}\n     */\n    public static void handle(IgnoreStrategy ignoreStrategy) {\n        IGNORE_STRATEGY_LOCAL.set(ignoreStrategy);\n    }\n\n    /**\n     * 清空本地忽略策略\n     */\n    public static void clearIgnoreStrategy() {\n        IGNORE_STRATEGY_LOCAL.remove();\n    }\n\n    /**\n     * 判断当前线程是否有忽略策略\n     *\n     * @return 是否有忽略策略\n     * @since 3.5.10\n     */\n    public static boolean hasIgnoreStrategy() {\n        return IGNORE_STRATEGY_LOCAL.get() != null;\n    }\n\n    /**\n     * 初始化缓存\n     * <p>\n     * Mapper 上 InterceptorIgnore 注解信息\n     *\n     * @param mapperClass Mapper Class\n     */\n    public synchronized static IgnoreStrategy initSqlParserInfoCache(Class<?> mapperClass) {\n        InterceptorIgnore ignore = mapperClass.getAnnotation(InterceptorIgnore.class);\n        if (ignore != null) {\n            String key = mapperClass.getName();\n            IgnoreStrategy cache = buildIgnoreStrategy(key, ignore);\n            IGNORE_STRATEGY_CACHE.put(key, cache);\n            return cache;\n        }\n        return null;\n    }\n\n    /**\n     * 获取忽略策略缓存信息\n     *\n     * @param key key\n     * @return 策略信息\n     * @since 3.5.10\n     */\n    public static IgnoreStrategy getIgnoreStrategy(String key) {\n        return IGNORE_STRATEGY_CACHE.get(key);\n    }\n\n    /**\n     * 按指定策略执行指定方法 (忽略线程级别,参数执行级使用最高)\n     * 方法执行完成后后释放掉当前线程上的忽略策略.\n     * <p>\n     * 注意:\n     * <li>1.不要和{@link #handle(IgnoreStrategy)}一起混合使用,此方法只是简化操作,防止未释放掉资源造成的错误<li/>\n     * <li>2.不要和{@link InterceptorIgnore} 注解一起搭配使用,例如在mapper上的default方法里再调用此方法,最终优先级还是以此方法为准<li/>\n     * <li>3.记住,一旦调用了此方法,开始会覆盖你当前执行线程上的策略,结束必定会释放掉当前线程上的策略</>\n     * </p>\n     *\n     * @param ignoreStrategy 忽略策略\n     * @param supplier       执行方法\n     * @param <T>            T\n     * @return 返回值\n     * @since 3.5.10\n     */\n    public static <T> T execute(IgnoreStrategy ignoreStrategy, Supplier<T> supplier) {\n        try {\n            handle(ignoreStrategy);\n            return supplier.get();\n        } finally {\n            clearIgnoreStrategy();\n        }\n    }\n\n    /**\n     * 按指定策略执行指定方法 (忽略线程级别,参数执行级使用最高)\n     * 方法执行完成后后释放掉当前线程上的忽略策略.\n     * <p>\n     * 注意:\n     * <li>1.不要和{@link #handle(IgnoreStrategy)}一起混合使用,此方法只是简化操作,防止未释放掉资源造成的错误<li/>\n     * <li>2.不要和{@link InterceptorIgnore} 注解一起搭配使用,例如在mapper上的default方法里再调用此方法,最终优先级还是以此方法为准<li/>\n     * <li>3.记住,一旦调用了此方法,开始会覆盖你当前执行线程上的策略,结束必定会释放掉当前线程上的策略</>\n     * </p>\n     *\n     * @param ignoreStrategy 忽略策略\n     * @param runnable       执行方法\n     * @since 3.5.10\n     */\n    public static void execute(IgnoreStrategy ignoreStrategy, Runnable runnable) {\n        try {\n            handle(ignoreStrategy);\n            runnable.run();\n        } finally {\n            clearIgnoreStrategy();\n        }\n    }\n\n    /**\n     * 通过方法获取策略信息(优先级方法注解>当前类注解)\n     *\n     * @param method 方法信息\n     * @return 忽略策略信息\n     * @see #initSqlParserInfoCache(Class)\n     * @see #initSqlParserInfoCache(IgnoreStrategy, String, Method)\n     * @since 3.5.10\n     */\n    public static IgnoreStrategy findIgnoreStrategy(Class<?> clz, Method method) {\n        String className = clz.getName();\n        IgnoreStrategy ignoreStrategy = getIgnoreStrategy(method.getDeclaringClass().getName() + StringPool.DOT + method.getName());\n        if (ignoreStrategy == null) {\n            ignoreStrategy = getIgnoreStrategy(className);\n        }\n        return ignoreStrategy;\n    }\n\n    /**\n     * 初始化缓存\n     * <p>\n     * Mapper#method 上 InterceptorIgnore 注解信息\n     *\n     * @param mapperAnnotation Mapper Class Name\n     * @param method           Method\n     */\n    public static void initSqlParserInfoCache(IgnoreStrategy mapperAnnotation, String mapperClassName, Method method) {\n        InterceptorIgnore ignoreStrategy = method.getAnnotation(InterceptorIgnore.class);\n        String key = mapperClassName.concat(StringPool.DOT).concat(method.getName());\n        String name = mapperClassName.concat(StringPool.HASH).concat(method.getName());\n        if (ignoreStrategy != null) {\n            IgnoreStrategy methodCache = buildIgnoreStrategy(name, ignoreStrategy);\n            if (mapperAnnotation == null) {\n                IGNORE_STRATEGY_CACHE.put(key, methodCache);\n                return;\n            }\n            IGNORE_STRATEGY_CACHE.put(key, chooseCache(mapperAnnotation, methodCache));\n        }\n    }\n\n    public static boolean willIgnoreTenantLine(String id) {\n        return willIgnore(id, IgnoreStrategy::getTenantLine);\n    }\n\n    public static boolean willIgnoreDynamicTableName(String id) {\n        return willIgnore(id, IgnoreStrategy::getDynamicTableName);\n    }\n\n    public static boolean willIgnoreBlockAttack(String id) {\n        return willIgnore(id, IgnoreStrategy::getBlockAttack);\n    }\n\n    public static boolean willIgnoreIllegalSql(String id) {\n        return willIgnore(id, IgnoreStrategy::getIllegalSql);\n    }\n\n    public static boolean willIgnoreDataPermission(String id) {\n        return willIgnore(id, IgnoreStrategy::getDataPermission);\n    }\n\n    public static boolean willIgnoreOthersByKey(String id, String key) {\n        return willIgnore(id, i -> CollectionUtils.isNotEmpty(i.getOthers()) && i.getOthers().getOrDefault(key, false));\n    }\n\n    public static boolean willIgnore(String id, Function<IgnoreStrategy, Boolean> function) {\n        // 1，优化获取本地忽略策略\n        IgnoreStrategy ignoreStrategy = IGNORE_STRATEGY_LOCAL.get();\n        if (null == ignoreStrategy) {\n            // 2，不存在取注解策略\n            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(id);\n        }\n        if (ignoreStrategy == null && id.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {\n            // 支持一下 selectKey\n            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(id.substring(0, id.length() - SelectKeyGenerator.SELECT_KEY_SUFFIX.length()));\n        }\n        if (ignoreStrategy == null) {\n            // fixed github issues/5342\n            int index = id.lastIndexOf(StringPool.DOT);\n            ignoreStrategy = IGNORE_STRATEGY_CACHE.get(index > 0 ? id.substring(0, index) : id);\n        }\n        if (ignoreStrategy != null) {\n            Boolean apply = function.apply(ignoreStrategy);\n            return apply != null && apply;\n        }\n        return false;\n    }\n\n    private static IgnoreStrategy chooseCache(IgnoreStrategy mapper, IgnoreStrategy method) {\n        return IgnoreStrategy.builder()\n            .tenantLine(chooseBoolean(mapper.getTenantLine(), method.getTenantLine()))\n            .dynamicTableName(chooseBoolean(mapper.getDynamicTableName(), method.getDynamicTableName()))\n            .blockAttack(chooseBoolean(mapper.getBlockAttack(), method.getBlockAttack()))\n            .illegalSql(chooseBoolean(mapper.getIllegalSql(), method.getIllegalSql()))\n            .dataPermission(chooseBoolean(mapper.getDataPermission(), method.getDataPermission()))\n            .others(chooseOthers(mapper.getOthers(), method.getOthers()))\n            .build();\n    }\n\n    private static IgnoreStrategy buildIgnoreStrategy(String name, InterceptorIgnore ignore) {\n        return IgnoreStrategy.builder()\n            .tenantLine(getBoolean(\"tenantLine\", name, ignore.tenantLine()))\n            .dynamicTableName(getBoolean(\"dynamicTableName\", name, ignore.dynamicTableName()))\n            .blockAttack(getBoolean(\"blockAttack\", name, ignore.blockAttack()))\n            .illegalSql(getBoolean(\"illegalSql\", name, ignore.illegalSql()))\n            .dataPermission(getBoolean(\"dataPermission\", name, ignore.dataPermission()))\n            .others(getOthers(name, ignore.others()))\n            .build();\n    }\n\n    private static Boolean getBoolean(String node, String name, String value) {\n        if (StringUtils.isBlank(value)) {\n            return null;\n        }\n        if (StringPool.ONE.equals(value) || StringPool.TRUE.equals(value) || StringPool.ON.equals(value)) {\n            return true;\n        }\n        if (StringPool.ZERO.equals(value) || StringPool.FALSE.equals(value) || StringPool.OFF.equals(value)) {\n            return false;\n        }\n        throw ExceptionUtils.mpe(\"unsupported value \\\"%s\\\" by `@InterceptorIgnore#%s` on top of \\\"%s\\\"\", value, node, name);\n    }\n\n    private static Map<String, Boolean> getOthers(String name, String[] values) {\n        if (ArrayUtils.isEmpty(values)) {\n            return null;\n        }\n        Map<String, Boolean> map = CollectionUtils.newHashMapWithExpectedSize(values.length);\n        for (String s : values) {\n            int index = s.indexOf(StringPool.AT);\n            Assert.isTrue(index > 0, \"unsupported value \\\"%s\\\" by `@InterceptorIgnore#others` on top of \\\"%s\\\"\", s, name);\n            String key = s.substring(0, index);\n            Boolean value = getBoolean(\"others\", name, s.substring(index + 1));\n            map.put(key, value);\n        }\n        return map;\n    }\n\n    /**\n     * mapper#method 上的注解 优先级大于 mapper 上的注解\n     */\n    private static Boolean chooseBoolean(Boolean mapper, Boolean method) {\n        if (mapper == null && method == null) {\n            return null;\n        }\n        if (method != null) {\n            return method;\n        }\n        return mapper;\n    }\n\n    private static Map<String, Boolean> chooseOthers(Map<String, Boolean> mapper, Map<String, Boolean> method) {\n        boolean emptyMapper = CollectionUtils.isEmpty(mapper);\n        boolean emptyMethod = CollectionUtils.isEmpty(method);\n        if (emptyMapper && emptyMethod) {\n            return null;\n        }\n        if (emptyMapper) {\n            return method;\n        }\n        if (emptyMethod) {\n            return mapper;\n        }\n        Set<String> mapperKeys = mapper.keySet();\n        Set<String> methodKeys = method.keySet();\n        Set<String> keys = new HashSet<>(mapperKeys.size() + methodKeys.size());\n        keys.addAll(methodKeys);\n        keys.addAll(mapperKeys);\n        Map<String, Boolean> map = CollectionUtils.newHashMapWithExpectedSize(keys.size());\n        methodKeys.forEach(k -> map.put(k, chooseBoolean(mapper.get(k), method.get(k))));\n        return map;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/spi/CompatibleHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.spi;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.util.ServiceLoader;\n\n/**\n * 兼容处理辅助类\n * <p>默认加载使用SPI实现,需要手动指定请使用{@link #setCompatibleSet(CompatibleSet)}</p>\n */\npublic class CompatibleHelper {\n\n    private static final Log LOG = LogFactory.getLog(CompatibleHelper.class);\n\n    private static CompatibleSet COMPATIBLE_SET = null;\n\n    static {\n        ServiceLoader<CompatibleSet> loader = ServiceLoader.load(CompatibleSet.class, CompatibleSet.class.getClassLoader());\n        int size = 0;\n        for (CompatibleSet compatibleSet : loader) {\n            size++;\n            LOG.debug(\"Load compatibleSet: \" + compatibleSet);\n            COMPATIBLE_SET = compatibleSet;\n        }\n        if (size > 1) {\n            LOG.warn(\"There are currently multiple implementations, and the last one is used \" + COMPATIBLE_SET);\n        }\n    }\n\n    /**\n     * 判断是否存在 {@link com.baomidou.mybatisplus.core.spi.CompatibleSet} 实例\n     *\n     * @return 是否存在 (存在返回true,为空返回false)\n     * @since 3.5.12\n     */\n    public static boolean hasCompatibleSet() {\n        return COMPATIBLE_SET != null;\n    }\n\n    /**\n     * 手动指定 {@link com.baomidou.mybatisplus.core.spi.CompatibleSet} 实例\n     *\n     * @param compatibleSet {@link com.baomidou.mybatisplus.core.spi.CompatibleSet} 实例\n     * @since 3.5.12\n     */\n    public static void setCompatibleSet(CompatibleSet compatibleSet) {\n        COMPATIBLE_SET = compatibleSet;\n    }\n\n    /**\n     * 获取{@link com.baomidou.mybatisplus.core.spi.CompatibleSet}实例\n     * <p>当为空时会抛出异常,需要检查是否为空请使用{@link #hasCompatibleSet()}</p>\n     *\n     * @return {@link com.baomidou.mybatisplus.core.spi.CompatibleSet}\n     * @see #setCompatibleSet(CompatibleSet)\n     */\n    public static CompatibleSet getCompatibleSet() {\n        Assert.isTrue(hasCompatibleSet(), \"Please add specific implementation dependencies or use the setCompatibleSet method to specify\");\n        return COMPATIBLE_SET;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/spi/CompatibleSet.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.spi;\n\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.io.InputStream;\nimport java.util.function.Consumer;\n\n/**\n * Web 开发平台待兼容方法集接口类\n */\npublic interface CompatibleSet {\n\n    SqlSession getSqlSession(SqlSessionFactory sessionFactory);\n\n    void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory);\n\n    boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Consumer<SqlSession> consumer);\n\n    /**\n     * @deprecated 3.5.12 无需实现\n     */\n    @Deprecated\n    default InputStream getInputStream(String path) throws Exception {\n        return null;\n    }\n\n    /**\n     * 获取容器bean实例\n     *\n     * @param clz 类型\n     * @return bean实例 (当无实例时返回null)\n     * @since 3.5.12\n     */\n    default <T> T getBean(Class<T> clz) {\n        return null;\n    }\n\n    /**\n     * 获取真实被代理的对象 (如果没有被代理,请返回原始对象)\n     *\n     * @param mapper Mapper对象\n     * @return 真实对象\n     * @since 3.5.12\n     */\n    default Object getProxyTargetObject(Object mapper) {\n        return null;\n    }\n\n    /**\n     * 传递上下文对象\n     * @param context 容器上下文\n     * @since 3.5.13\n     */\n    default void setContext(Object context) {\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/AES.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.Random;\n\n/**\n * AES CBC模式加密工具类\n *\n * @author hubin\n * @since 2020-05-23\n */\npublic class AES {\n\n    private static final String CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n    /**\n     * 加密\n     *\n     * @param data 需要加密的内容\n     * @param key  加密密码\n     * @return 加密内容\n     */\n    public static byte[] encrypt(byte[] data, byte[] key) {\n        try {\n            SecretKeySpec secretKey = new SecretKeySpec(key, Constants.AES);\n            byte[] enCodeFormat = secretKey.getEncoded();\n            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, Constants.AES);\n            Cipher cipher = Cipher.getInstance(Constants.AES_CBC_CIPHER);\n            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(key));\n            return cipher.doFinal(data);\n        } catch (Exception e) {\n            throw new MybatisPlusException(e);\n        }\n    }\n\n    /**\n     * 解密\n     *\n     * @param data 待解密内容\n     * @param key  解密密钥\n     * @return 解密内容\n     */\n    public static byte[] decrypt(byte[] data, byte[] key) {\n        try {\n            SecretKeySpec secretKey = new SecretKeySpec(key, Constants.AES);\n            byte[] enCodeFormat = secretKey.getEncoded();\n            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, Constants.AES);\n            Cipher cipher = Cipher.getInstance(Constants.AES_CBC_CIPHER);\n            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(key));\n            return cipher.doFinal(data);\n        } catch (Exception e) {\n            throw new MybatisPlusException(e);\n        }\n    }\n\n    /**\n     * 加密\n     *\n     * @param data 需要加密的内容\n     * @param key  加密密码\n     * @return 加密内容\n     */\n    public static String encrypt(String data, String key) {\n        byte[] valueByte = encrypt(data.getBytes(StandardCharsets.UTF_8), key.getBytes(StandardCharsets.UTF_8));\n        return Base64.getEncoder().encodeToString(valueByte);\n    }\n\n    /**\n     * 解密\n     *\n     * @param data 待解密内容 base64 字符串\n     * @param key  解密密钥\n     * @return 解密内容\n     */\n    public static String decrypt(String data, String key) {\n        byte[] originalData = Base64.getDecoder().decode(data.getBytes());\n        byte[] valueByte = decrypt(originalData, key.getBytes(StandardCharsets.UTF_8));\n        return new String(valueByte);\n    }\n\n    /**\n     * 生成一个随机字符串密钥\n     *\n     * @return 密钥\n     */\n    public static String generateRandomKey() {\n        Random random = new Random();\n        StringBuilder buffer = new StringBuilder();\n        int keySize = 16;\n        int length = CHARS.length();\n        while (keySize-- != 0) {\n            buffer.append(CHARS.charAt(random.nextInt(length)));\n        }\n        return buffer.toString();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/AnnotationUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport lombok.experimental.UtilityClass;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author nieqiurong\n * @since 3.5.6\n */\n@UtilityClass\npublic class AnnotationUtils {\n\n    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Field field) {\n        return getAnnotation(annotationClazz, new HashSet<>(), field.getDeclaredAnnotations());\n    }\n\n    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Class<?> clz) {\n        Set<Class<? extends Annotation>> hashSet = new HashSet<>();\n        T annotation = getAnnotation(annotationClazz, hashSet, clz.getDeclaredAnnotations());\n        if (annotation != null) {\n            return annotation;\n        }\n        Class<?> currentClass = clz.getSuperclass();\n        while (currentClass != null) {\n            annotation = getAnnotation(annotationClazz, hashSet, currentClass.getDeclaredAnnotations());\n            if (annotation != null) {\n                return annotation;\n            }\n            currentClass = currentClass.getSuperclass();\n        }\n        return null;\n    }\n\n    public <T extends Annotation> T findFirstAnnotation(Class<T> annotationClazz, Method method) {\n        return getAnnotation(annotationClazz, new HashSet<>(), method.getDeclaredAnnotations());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T extends Annotation> T getAnnotation(Class<T> annotationClazz, Set<Class<? extends Annotation>> annotationSet, Annotation... annotations) {\n        for (Annotation annotation : annotations) {\n            if (annotationSet.add(annotation.annotationType())) {\n                if (annotationClazz.isAssignableFrom(annotation.annotationType())) {\n                    return (T) annotation;\n                }\n                annotation = getAnnotation(annotationClazz, annotationSet, annotation.annotationType().getDeclaredAnnotations());\n                if (annotation != null) {\n                    return (T) annotation;\n                }\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/AopUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.lang.reflect.Field;\n\n/**\n * AopUtils Aop工具类\n *\n * @author Caratacus\n * @since  2018-08-02\n */\npublic class\nAopUtils {\n\n    /**\n     * 是否加载Spring-Aop模块\n     *\n     * @since 3.5.4\n     */\n    private static boolean loadAop = false;\n\n    static {\n        try {\n            ClassUtils.toClassConfident(\"org.springframework.aop.framework.AopProxyUtils\");\n            loadAop = true;\n        } catch (Exception exception) {\n            // ignore\n        }\n    }\n\n    private static final Log logger = LogFactory.getLog(AopUtils.class);\n\n    /**\n     * 是否加载Spring-Aop模块\n     *\n     * @return 是否加载Spring-Aop模块\n     * @since 3.5.5\n     */\n    public static boolean isLoadSpringAop() {\n        return loadAop;\n    }\n\n\n    /**\n     * 获取源目标对象\n     *\n     * @param proxy ignore\n     * @param <T> ignore\n     * @return ignore\n     */\n    public static <T> T getTargetObject(T proxy) {\n        if (!ClassUtils.isProxy(proxy.getClass())) {\n            return proxy;\n        }\n        try {\n            if (org.springframework.aop.support.AopUtils.isJdkDynamicProxy(proxy)) {\n                return getJdkDynamicProxyTargetObject(proxy);\n            } else if (org.springframework.aop.support.AopUtils.isCglibProxy(proxy)) {\n                return getCglibProxyTargetObject(proxy);\n            } else {\n                logger.warn(\"Warn: The proxy object processing method is not supported.\");\n                return proxy;\n            }\n        } catch (Exception e) {\n            throw ExceptionUtils.mpe(\"Error: Get proxy targetObject exception !  Cause:\" + e);\n        }\n    }\n\n    /**\n     * 获取Cglib源目标对象\n     *\n     * @param proxy ignore\n     * @param <T> ignore\n     * @return ignore\n     */\n    @SuppressWarnings(\"unchecked\")\n\tprivate static <T> T getCglibProxyTargetObject(T proxy) throws Exception {\n        Field cglibField = proxy.getClass().getDeclaredField(\"CGLIB$CALLBACK_0\");\n        cglibField.setAccessible(true);\n        Object dynamicAdvisedInterceptor = cglibField.get(proxy);\n        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField(\"advised\");\n        advised.setAccessible(true);\n        Object target = ((org.springframework.aop.framework.AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();\n        return (T) target;\n    }\n\n    /**\n     * 获取JdkDynamic源目标对象\n     *\n     * @param proxy ignore\n     * @param <T> ignore\n     * @return ignore\n     */\n    @SuppressWarnings(\"unchecked\")\n\tprivate static <T> T getJdkDynamicProxyTargetObject(T proxy) throws Exception {\n        Field jdkDynamicField = proxy.getClass().getSuperclass().getDeclaredField(\"jdkDynamicField\");\n        jdkDynamicField.setAccessible(true);\n        org.springframework.aop.framework.AopProxy aopProxy = (org.springframework.aop.framework.AopProxy) jdkDynamicField.get(proxy);\n        Field advised = aopProxy.getClass().getDeclaredField(\"advised\");\n        advised.setAccessible(true);\n        Object target = ((org.springframework.aop.framework.AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();\n        return (T) target;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ArrayUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\n/**\n * <p>\n * ArrayUtils工具类\n * </p>\n *\n * @author Caratacus\n * @since 2017-03-09\n */\npublic final class ArrayUtils {\n\n    private ArrayUtils() {\n    }\n\n    /**\n     * 判断数据是否为空\n     *\n     * @param array 长度\n     * @return 数组对象为null或者长度为 0 时，返回 false\n     */\n    public static boolean isEmpty(Object[] array) {\n        return array == null || array.length == 0;\n    }\n\n    /**\n     * 判断数组是否不为空\n     *\n     * @param array 数组\n     * @return 数组对象内含有任意对象时返回 true\n     * @see ArrayUtils#isEmpty(Object[])\n     */\n    public static boolean isNotEmpty(Object[] array) {\n        return !isEmpty(array);\n    }\n\n\n    /**\n     * 判断是否为数组\n     *\n     * @param obj 对象\n     * @return 是否为数组\n     */\n    public static boolean isArray(Object obj) {\n        return obj != null && obj.getClass().isArray();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/Assert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * 断言类\n *\n * @author miemie\n * @since 2018-07-24\n */\npublic final class Assert {\n\n    /**\n     * 断言这个 boolean 为 true\n     * <p>为 false 则抛出异常</p>\n     *\n     * @param expression boolean 值\n     * @param message    消息\n     */\n    public static void isTrue(boolean expression, String message, Object... params) {\n        if (!expression) {\n            throw ExceptionUtils.mpe(message, params);\n        }\n    }\n\n    /**\n     * 断言这个 boolean 为 false\n     * <p>为 true 则抛出异常</p>\n     *\n     * @param expression boolean 值\n     * @param message    消息\n     */\n    public static void isFalse(boolean expression, String message, Object... params) {\n        isTrue(!expression, message, params);\n    }\n\n    /**\n     * 断言这个 object 为 null\n     * <p>不为 null 则抛异常</p>\n     *\n     * @param object  对象\n     * @param message 消息\n     */\n    public static void isNull(Object object, String message, Object... params) {\n        isTrue(object == null, message, params);\n    }\n\n    /**\n     * 断言这个 object 不为 null\n     * <p>为 null 则抛异常</p>\n     *\n     * @param object  对象\n     * @param message 消息\n     */\n    public static void notNull(Object object, String message, Object... params) {\n        isTrue(object != null, message, params);\n    }\n\n    /**\n     * 断言这个 value 不为 empty\n     * <p>为 empty 则抛异常</p>\n     *\n     * @param value   字符串\n     * @param message 消息\n     */\n    public static void notEmpty(String value, String message, Object... params) {\n        isTrue(StringUtils.isNotBlank(value), message, params);\n    }\n\n    /**\n     * 断言这个 collection 不为 empty\n     * <p>为 empty 则抛异常</p>\n     *\n     * @param collection 集合\n     * @param message    消息\n     */\n    public static void notEmpty(Collection<?> collection, String message, Object... params) {\n        isTrue(CollectionUtils.isNotEmpty(collection), message, params);\n    }\n\n    /**\n     * 断言这个 map 不为 empty\n     * <p>为 empty 则抛异常</p>\n     *\n     * @param map     集合\n     * @param message 消息\n     */\n    public static void notEmpty(Map<?, ?> map, String message, Object... params) {\n        isTrue(CollectionUtils.isNotEmpty(map), message, params);\n    }\n\n    /**\n     * 断言这个 map 为 empty\n     * <p>为 empty 则抛异常</p>\n     *\n     * @param map     集合\n     * @param message 消息\n     */\n    public static void isEmpty(Map<?, ?> map, String message, Object... params) {\n        isTrue(CollectionUtils.isEmpty(map), message, params);\n    }\n\n    /**\n     * 断言这个 数组 不为 empty\n     * <p>为 empty 则抛异常</p>\n     *\n     * @param array   数组\n     * @param message 消息\n     */\n    public static void notEmpty(Object[] array, String message, Object... params) {\n        isTrue(ArrayUtils.isNotEmpty(array), message, params);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/BeanUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport net.sf.cglib.beans.BeanMap;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport static java.util.stream.Collectors.toList;\n\n/**\n * Bean 转换工具类\n * <p>使用请依赖 cglib 包</p>\n *\n * @author hubin HCL\n * @since 2018-06-12\n */\npublic final class BeanUtils {\n\n    private BeanUtils() {\n    }\n\n    /**\n     * 将对象装换为 map,对象转成 map，key肯定是字符串\n     *\n     * @param bean 转换对象\n     * @return 返回转换后的 map 对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static Map<String, Object> beanToMap(Object bean) {\n        return null == bean ? null : BeanMap.create(bean);\n    }\n\n    /**\n     * map 转换为 java bean 对象\n     *\n     * @param map   转换 MAP\n     * @param clazz 对象 Class\n     * @return 返回 bean 对象\n     */\n    public static <T> T mapToBean(Map<String, ?> map, Class<T> clazz) {\n        T bean = ClassUtils.newInstance(clazz);\n        BeanMap.create(bean).putAll(map);\n        return bean;\n    }\n\n    /**\n     * List&lt;T&gt; 转换为 List&lt;Map&lt;String, Object&gt;&gt;\n     *\n     * @param beans 转换对象集合\n     * @return 返回转换后的 bean 列表\n     */\n    public static <T> List<Map<String, Object>> beansToMaps(List<T> beans) {\n        if (CollectionUtils.isEmpty(beans)) {\n            return Collections.emptyList();\n        }\n        return beans.stream().map(BeanUtils::beanToMap).collect(toList());\n    }\n\n    /**\n     * List&lt;Map&lt;String, Object&gt;&gt; 转换为 List&lt;T&gt;\n     *\n     * @param maps  转换 MAP 集合\n     * @param clazz 对象 Class\n     * @return 返回转换后的 bean 集合\n     */\n    public static <T> List<T> mapsToBeans(List<? extends Map<String, ?>> maps, Class<T> clazz) {\n        if (CollectionUtils.isEmpty(maps)) {\n            return Collections.emptyList();\n        }\n        return maps.stream().map(e -> mapToBean(e, clazz)).collect(toList());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ClassUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport org.apache.ibatis.io.Resources;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * <p>\n * ClassUtils\n * </p>\n *\n * @author Caratacus\n * @author HCL\n * @since 2017/07/08\n */\npublic final class ClassUtils {\n\n    private static ClassLoader systemClassLoader;\n\n    static {\n        try {\n            systemClassLoader = ClassLoader.getSystemClassLoader();\n        } catch (SecurityException ignored) {\n            // AccessControlException on Google App Engine\n        }\n    }\n\n    /**\n     * 代理 class 的名称\n     */\n    private static final List<String> PROXY_CLASS_NAMES = Arrays.asList(\"net.sf.cglib.proxy.Factory\"\n        // cglib\n        , \"org.springframework.cglib.proxy.Factory\"\n        , \"javassist.util.proxy.ProxyObject\"\n        // javassist\n        , \"org.apache.ibatis.javassist.util.proxy.ProxyObject\");\n\n    private ClassUtils() {\n    }\n\n    /**\n     * 判断传入的类型是否是布尔类型\n     *\n     * @param type 类型\n     * @return 如果是原生布尔或者包装类型布尔，均返回 true\n     */\n    public static boolean isBoolean(Class<?> type) {\n        return type == boolean.class || Boolean.class == type;\n    }\n\n    /**\n     * 判断是否为代理对象\n     *\n     * @param clazz 传入 class 对象\n     * @return 如果对象class是代理 class，返回 true\n     */\n    public static boolean isProxy(Class<?> clazz) {\n        if (clazz != null) {\n            for (Class<?> cls : clazz.getInterfaces()) {\n                if (PROXY_CLASS_NAMES.contains(cls.getName())) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * <p>\n     * 获取当前对象的 class\n     * </p>\n     *\n     * @param clazz 传入\n     * @return 如果是代理的class，返回父 class，否则返回自身\n     */\n    public static Class<?> getUserClass(Class<?> clazz) {\n        Assert.notNull(clazz, \"Class must not be null\");\n        return isProxy(clazz) ? clazz.getSuperclass() : clazz;\n    }\n\n    /**\n     * <p>\n     * 获取当前对象的class\n     * </p>\n     *\n     * @param object 对象\n     * @return 返回对象的 user class\n     */\n    public static Class<?> getUserClass(Object object) {\n        Assert.notNull(object, \"Instance must not be null\");\n        return getUserClass(object.getClass());\n    }\n\n    /**\n     * <p>\n     * 根据指定的 class ， 实例化一个对象，根据构造参数来实例化\n     * </p>\n     * <p>\n     * 在 java9 及其之后的版本 Class.newInstance() 方法已被废弃\n     * </p>\n     *\n     * @param clazz 需要实例化的对象\n     * @param <T>   类型，由输入类型决定\n     * @return 返回新的实例\n     */\n    public static <T> T newInstance(Class<T> clazz) {\n        try {\n            Constructor<T> constructor = clazz.getDeclaredConstructor();\n            constructor.setAccessible(true);\n            return constructor.newInstance();\n        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |\n                 NoSuchMethodException e) {\n            throw ExceptionUtils.mpe(\"实例化对象时出现错误,请尝试给 %s 添加无参的构造方法\", e, clazz.getName());\n        }\n    }\n\n    /**\n     * 实例化对象.\n     *\n     * @param clazzName 全类名\n     * @param <T>       类型\n     * @return 实例\n     * @since 3.3.2\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T newInstance(String clazzName) {\n        return (T) newInstance(toClassConfident(clazzName));\n    }\n\n\n    /**\n     * <p>\n     * 请仅在确定类存在的情况下调用该方法\n     * </p>\n     *\n     * @param name 全类名\n     * @return 返回转换后的 Class\n     */\n    public static Class<?> toClassConfident(String name) {\n        return toClassConfident(name, null);\n    }\n\n    /**\n     * @param name        全类名\n     * @param classLoader 类加载器\n     * @return Class信息\n     * @since 3.4.3\n     */\n    public static Class<?> toClassConfident(String name, ClassLoader classLoader) {\n        try {\n            return loadClass(name, getClassLoaders(classLoader));\n        } catch (ClassNotFoundException e) {\n            throw ExceptionUtils.mpe(\"找不到指定的class！请仅在明确确定会有 class 的时候，调用该方法\", e);\n        }\n    }\n\n    private static Class<?> loadClass(String className, ClassLoader[] classLoaders) throws ClassNotFoundException {\n        for (ClassLoader classLoader : classLoaders) {\n            if (classLoader != null) {\n                try {\n                    return Class.forName(className, true, classLoader);\n                } catch (ClassNotFoundException e) {\n                    // ignore\n                }\n            }\n        }\n        throw new ClassNotFoundException(\"Cannot find class: \" + className);\n    }\n\n\n    /**\n     * Determine the name of the package of the given class,\n     * e.g. \"java.lang\" for the {@code java.lang.String} class.\n     *\n     * @param clazz the class\n     * @return the package name, or the empty String if the class\n     * is defined in the default package\n     */\n    public static String getPackageName(Class<?> clazz) {\n        Assert.notNull(clazz, \"Class must not be null\");\n        return getPackageName(clazz.getName());\n    }\n\n    /**\n     * Determine the name of the package of the given fully-qualified class name,\n     * e.g. \"java.lang\" for the {@code java.lang.String} class name.\n     *\n     * @param fqClassName the fully-qualified class name\n     * @return the package name, or the empty String if the class\n     * is defined in the default package\n     */\n    public static String getPackageName(String fqClassName) {\n        Assert.notNull(fqClassName, \"Class name must not be null\");\n        int lastDotIndex = fqClassName.lastIndexOf(StringPool.DOT);\n        return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : StringPool.EMPTY);\n    }\n\n    /**\n     * Return the default ClassLoader to use: typically the thread context\n     * ClassLoader, if available; the ClassLoader that loaded the ClassUtils\n     * class will be used as fallback.\n     * <p>Call this method if you intend to use the thread context ClassLoader\n     * in a scenario where you clearly prefer a non-null ClassLoader reference:\n     * for example, for class path resource loading (but not necessarily for\n     * {@code Class.forName}, which accepts a {@code null} ClassLoader\n     * reference as well).\n     *\n     * @return the default ClassLoader (only {@code null} if even the system\n     * ClassLoader isn't accessible)\n     * @see Thread#getContextClassLoader()\n     * @see ClassLoader#getSystemClassLoader()\n     * @since 3.3.2\n     */\n    @Deprecated\n    public static ClassLoader getDefaultClassLoader() {\n        ClassLoader cl = null;\n        try {\n            cl = Thread.currentThread().getContextClassLoader();\n        } catch (Throwable ex) {\n            // Cannot access thread context ClassLoader - falling back...\n        }\n        if (cl == null) {\n            // No thread context class loader -> use class loader of this class.\n            cl = ClassUtils.class.getClassLoader();\n            if (cl == null) {\n                // getClassLoader() returning null indicates the bootstrap ClassLoader\n                try {\n                    cl = ClassLoader.getSystemClassLoader();\n                } catch (Throwable ex) {\n                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...\n                }\n            }\n        }\n        return cl;\n    }\n\n    private static ClassLoader[] getClassLoaders(ClassLoader classLoader) {\n        return new ClassLoader[]{\n            classLoader,\n            Resources.getDefaultClassLoader(),\n            Thread.currentThread().getContextClassLoader(),\n            ClassUtils.class.getClassLoader(),\n            systemClassLoader};\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/CollectionUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * Collection工具类\n *\n * @author Caratacus\n * @since 2016-09-19\n */\npublic class CollectionUtils {\n\n    private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);\n\n    private static boolean isJdk8;\n\n    static {\n        // Java 8\n        // Java 9+: 9,11,17\n        try {\n            isJdk8 = System.getProperty(\"java.version\").startsWith(\"1.8.\");\n        } catch (Exception ignore) {\n            isJdk8 = true;\n        }\n    }\n\n    /**\n     * 校验集合是否为空\n     *\n     * @param coll 入参\n     * @return boolean\n     */\n    public static boolean isEmpty(Collection<?> coll) {\n        return (coll == null || coll.isEmpty());\n    }\n\n    /**\n     * 校验集合是否不为空\n     *\n     * @param coll 入参\n     * @return boolean\n     */\n    public static boolean isNotEmpty(Collection<?> coll) {\n        return !isEmpty(coll);\n    }\n\n    /**\n     * 判断Map是否为空\n     *\n     * @param map 入参\n     * @return boolean\n     */\n    public static boolean isEmpty(Map<?, ?> map) {\n        return (map == null || map.isEmpty());\n    }\n\n    /**\n     * 判断Map是否不为空\n     *\n     * @param map 入参\n     * @return boolean\n     */\n    public static boolean isNotEmpty(Map<?, ?> map) {\n        return !isEmpty(map);\n    }\n\n    /**\n     * 创建默认HashMap\n     *\n     * @param <K> K\n     * @param <V> V\n     * @return HashMap\n     * @see com.google.common.collect.Maps#newHashMap()\n     * @since 3.4.0\n     */\n    public static <K, V> HashMap<K, V> newHashMap() {\n        return new HashMap<>();\n    }\n\n    /**\n     * 根据预期大小创建HashMap.\n     *\n     * @param expectedSize 预期大小\n     * @param <K>          K\n     * @param <V>          V\n     * @return HashMap\n     * @see com.google.common.collect.Maps#newHashMapWithExpectedSize\n     * @since 3.4.0\n     */\n    public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {\n        return new HashMap<>(capacity(expectedSize));\n    }\n\n    /**\n     * 用来过渡下Jdk1.8下ConcurrentHashMap的性能bug\n     * <a href=\"https://bugs.openjdk.java.net/browse/JDK-8161372\">JDK-8161372</a>\n     * <p>\n     *  A temporary workaround for Java 8 ConcurrentHashMap#computeIfAbsent specific performance issue: JDK-8161372.</br>\n     *  @see <a href=\"https://bugs.openjdk.java.net/browse/JDK-8161372\">https://bugs.openjdk.java.net/browse/JDK-8161372</a>\n     *\n     * @param concurrentHashMap ConcurrentHashMap 没限制类型了，非ConcurrentHashMap就别调用这方法了\n     * @param key               key\n     * @param mappingFunction   function\n     * @param <K>               k\n     * @param <V>               v\n     * @return V\n     * @since 3.4.0\n     */\n    public static <K, V> V computeIfAbsent(Map<K, V> concurrentHashMap, K key, Function<? super K, ? extends V> mappingFunction) {\n        Objects.requireNonNull(mappingFunction);\n        if (isJdk8) {\n            V v = concurrentHashMap.get(key);\n            if (null == v) {\n                // issue#11986 lock bug\n                // v = map.computeIfAbsent(key, func);\n\n                // this bug fix methods maybe cause `func.apply` multiple calls.\n                v = mappingFunction.apply(key);\n                if (null == v) {\n                    return null;\n                }\n                final V res = concurrentHashMap.putIfAbsent(key, v);\n                if (null != res) {\n                    // if pre value present, means other thread put value already, and putIfAbsent not effect\n                    // return exist value\n                    return res;\n                }\n                // if pre value is null, means putIfAbsent effected, return current value\n            }\n            return v;\n        } else {\n            return concurrentHashMap.computeIfAbsent(key, mappingFunction);\n        }\n\n    }\n\n    /**\n     * Returns a capacity that is sufficient to keep the map from being resized as\n     * long as it grows no larger than expectedSize and the load factor is >= its\n     * default (0.75).\n     *\n     * @see com.google.common.collect.Maps#capacity(int)\n     * @since 3.4.0\n     */\n    private static int capacity(int expectedSize) {\n        if (expectedSize < 3) {\n            if (expectedSize < 0) {\n                throw new IllegalArgumentException(\"expectedSize cannot be negative but was: \" + expectedSize);\n            }\n            return expectedSize + 1;\n        }\n        if (expectedSize < MAX_POWER_OF_TWO) {\n            // This is the calculation used in JDK8 to resize when a putAll\n            // happens; it seems to be the most conservative calculation we\n            // can make.  0.75 is the default load factor.\n            return (int) ((float) expectedSize / 0.75F + 1.0F);\n        }\n        return Integer.MAX_VALUE; // any large value\n    }\n\n    // 提供处理Map多key取值工具方法\n\n    /**\n     * 批量取出Map中的值\n     *\n     * @param map  map\n     * @param keys 键的集合\n     * @param <K>  key的泛型\n     * @param <V>  value的泛型\n     * @return value的泛型的集合\n     */\n    public static <K, V> List<V> getCollection(Map<K, V> map, Iterable<K> keys) {\n        List<V> result = new ArrayList<>();\n        if (map != null && !map.isEmpty() && keys != null) {\n            keys.forEach(key -> Optional.ofNullable(map.get(key)).ifPresent(result::add));\n        }\n        return result;\n    }\n\n    /**\n     * 批量取出Map中的值\n     *\n     * @param map        map\n     * @param keys       键的集合\n     * @param comparator 排序器\n     * @param <K>        key的泛型\n     * @param <V>        value的泛型\n     * @return value的泛型的集合\n     */\n    public static <K, V> List<V> getCollection(Map<K, V> map, Iterable<K> keys, Comparator<V> comparator) {\n        Objects.requireNonNull(comparator);\n        List<V> result = getCollection(map, keys);\n        result.sort(comparator);\n        return result;\n    }\n\n    /**\n     * 构建List\n     *\n     * @since 3.5.4\n     */\n    @SafeVarargs\n    public static <T> List<T> toList(T... t) {\n        if (t != null) {\n            return Arrays.asList(t);\n        }\n        return Collections.emptyList();\n    }\n\n    /**\n     * 切割集合为多个集合\n     * @param entityList 数据集合\n     * @param batchSize 每批集合的大小\n     * @return 切割后的多个集合\n     * @param <T> 数据类型\n     */\n    public static <T> List<List<T>> split(Collection<T> entityList, int batchSize) {\n        if (isEmpty(entityList)) {\n            return Collections.emptyList();\n        }\n        Assert.isFalse(batchSize < 1, \"batchSize must not be less than one\");\n        final Iterator<T> iterator = entityList.iterator();\n        final List<List<T>> results = new ArrayList<>(entityList.size() / batchSize);\n        while (iterator.hasNext()) {\n            final List<T> list = IntStream.range(0, batchSize).filter(x -> iterator.hasNext())\n                .mapToObj(i -> iterator.next()).collect(Collectors.toList());\n            if (!list.isEmpty()) {\n                results.add(list);\n            }\n        }\n        return results;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/Constants.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.io.Serializable;\n\n/**\n * mybatis_plus 自用常量集中管理\n *\n * @author miemie\n * @since 2018-07-22\n */\npublic interface Constants extends StringPool, Serializable {\n\n    /**\n     * project name\n     */\n    String MYBATIS_PLUS = \"mybatis-plus\";\n\n    /**\n     * MD5\n     */\n    String MD5 = \"MD5\";\n    /**\n     * AES\n     */\n    String AES = \"AES\";\n    /**\n     * AES 算法\n     */\n    String AES_CBC_CIPHER = \"AES/CBC/PKCS5Padding\";\n    /**\n     * as\n     */\n    String AS = \" AS \";\n\n\n    /**\n     * 实体类\n     */\n    String ENTITY = \"et\";\n\n    /**\n     * 填充实体\n     *\n     * @since 3.5.8\n     */\n    String MP_FILL_ET = \"mpFillEt\";\n\n    /**\n     * 实体类 带后缀 ==> .\n     */\n    String ENTITY_DOT = ENTITY + DOT;\n    /**\n     * wrapper 类\n     */\n    String WRAPPER = \"ew\";\n    /**\n     * wrapper 类 带后缀 ==> .\n     */\n    String WRAPPER_DOT = WRAPPER + DOT;\n    /**\n     * wrapper 类的属性 entity\n     */\n    String WRAPPER_ENTITY = WRAPPER_DOT + \"entity\";\n    /**\n     * wrapper 类的属性 sqlSegment\n     */\n    String WRAPPER_SQLSEGMENT = WRAPPER_DOT + \"sqlSegment\";\n    /**\n     * wrapper 类的属性 emptyOfNormal\n     */\n    String WRAPPER_EMPTYOFNORMAL = WRAPPER_DOT + \"emptyOfNormal\";\n    /**\n     * wrapper 类的属性 nonEmptyOfNormal\n     */\n    String WRAPPER_NONEMPTYOFNORMAL = WRAPPER_DOT + \"nonEmptyOfNormal\";\n    /**\n     * wrapper 类的属性 nonEmptyOfEntity\n     */\n    String WRAPPER_NONEMPTYOFENTITY = WRAPPER_DOT + \"nonEmptyOfEntity\";\n    /**\n     * wrapper 类的属性 emptyOfWhere\n     */\n    String WRAPPER_EMPTYOFWHERE = WRAPPER_DOT + \"emptyOfWhere\";\n    /**\n     * wrapper 类的判断属性 nonEmptyOfWhere\n     */\n    String WRAPPER_NONEMPTYOFWHERE = WRAPPER_DOT + \"nonEmptyOfWhere\";\n    /**\n     * wrapper 类的属性 entity 带后缀 ==> .\n     */\n    String WRAPPER_ENTITY_DOT = WRAPPER_DOT + \"entity\" + DOT;\n    /**\n     * wrapper 类的属性 expression 下级属性 order\n     */\n    String WRAPPER_EXPRESSION_ORDER = WRAPPER_DOT + \"useAnnotationOrderBy\";\n    /**\n     * UpdateWrapper 类的属性 sqlSet\n     */\n    String U_WRAPPER_SQL_SET = WRAPPER_DOT + \"sqlSet\";\n    /**\n     * QueryWrapper 类的属性 sqlSelect\n     */\n    String Q_WRAPPER_SQL_SELECT = WRAPPER_DOT + \"sqlSelect\";\n    /**\n     * wrapper 类的属性 sqlComment\n     */\n    String Q_WRAPPER_SQL_COMMENT = WRAPPER_DOT + \"sqlComment\";\n    /**\n     * wrapper 类的属性 sqlFirst\n     */\n    String Q_WRAPPER_SQL_FIRST = WRAPPER_DOT + \"sqlFirst\";\n    /**\n     * columnMap\n     */\n    @Deprecated\n    String COLUMN_MAP = \"cm\";\n    /**\n     * columnMap.isEmpty\n     */\n    String COLUMN_MAP_IS_EMPTY = COLUMN_MAP + DOT + \"isEmpty\";\n    /**\n     * collection\n     *\n     * @see #COLL\n     * @deprecated 3.5.2 后面修改成collection\n     */\n    @Deprecated\n    String COLLECTION = \"coll\";\n\n    /**\n     * @since 3.5.2\n     */\n    String COLL = \"coll\";\n    /**\n     * list\n     *\n     * @since 3.5.0\n     */\n    String LIST = \"list\";\n    /**\n     * where\n     */\n    String WHERE = \"WHERE\";\n    /**\n     * limit\n     */\n    String LIMIT = \"LIMIT\";\n\n    /**\n     * @since 3.5.2\n     */\n    String ARRAY = \"array\";\n    /**\n     * order by\n     */\n    String ORDER_BY = \"ORDER BY\";\n    /**\n     * asc\n     */\n    String ASC = \"ASC\";\n    /**\n     * desc\n     */\n    String DESC = \"DESC\";\n    /**\n     * 乐观锁字段\n     */\n    String MP_OPTLOCK_VERSION_ORIGINAL = \"MP_OPTLOCK_VERSION_ORIGINAL\";\n\n    /**\n     * wrapper 内部参数相关\n     */\n    String WRAPPER_PARAM = \"MPGENVAL\";\n    String WRAPPER_PARAM_MIDDLE = \".paramNameValuePairs\" + DOT;\n\n\n    /**\n     * 默认批次提交数量\n     */\n    int DEFAULT_BATCH_SIZE = 1000;\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/EncryptUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.Base64;\n\n/**\n * 加密工具类\n *\n * @author hubin\n * @since 2018-08-02\n */\npublic class EncryptUtils {\n\n    /**\n     * MD5 Base64 加密\n     *\n     * @param str 待加密的字符串\n     * @return 加密后的字符串\n     */\n    public static String md5Base64(String str) {\n        //确定计算方法\n        try {\n            MessageDigest md5 = MessageDigest.getInstance(Constants.MD5);\n            //加密后的字符串\n            byte[] src = md5.digest(str.getBytes(StandardCharsets.UTF_8));\n            return Base64.getEncoder().encodeToString(src);\n        } catch (Exception e) {\n            throw ExceptionUtils.mpe(e);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ExceptionUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\n\n/**\n * 异常辅助工具类\n *\n * @author HCL\n * @since 2018-07-24\n */\npublic final class ExceptionUtils {\n\n    private ExceptionUtils() {\n    }\n\n    /**\n     * 返回一个新的异常，统一构建，方便统一处理\n     *\n     * @param msg 消息\n     * @param t   异常信息\n     * @return 返回异常\n     */\n    public static MybatisPlusException mpe(String msg, Throwable t, Object... params) {\n        return new MybatisPlusException(String.format(msg, params), t);\n    }\n\n    /**\n     * 重载的方法\n     *\n     * @param msg 消息\n     * @return 返回异常\n     */\n    public static MybatisPlusException mpe(String msg, Object... params) {\n        return new MybatisPlusException(String.format(msg, params));\n    }\n\n    /**\n     * 重载的方法\n     *\n     * @param t 异常\n     * @return 返回异常\n     */\n    public static MybatisPlusException mpe(Throwable t) {\n        return new MybatisPlusException(t);\n    }\n\n    public static void throwMpe(boolean condition, String msg, Object... params) {\n        if (condition) {\n            throw mpe(msg, params);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/GlobalConfigUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.injector.ISqlInjector;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Mybatis全局缓存工具类\n *\n * @author Caratacus\n * @since 2017-06-15\n */\npublic class GlobalConfigUtils {\n\n    /**\n     * 缓存全局信息\n     */\n    private static final Map<String, GlobalConfig> GLOBAL_CONFIG = new ConcurrentHashMap<>();\n\n    /**\n     * 获取当前的SqlSessionFactory\n     *\n     * @param clazz 实体类\n     * @deprecated 3.5.3.2 尽量少用,后期取消此方法获取实例\n     */\n    @Deprecated\n    public static SqlSessionFactory currentSessionFactory(Class<?> clazz) {\n        Assert.notNull(clazz, \"Class must not be null\");\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        Assert.notNull(tableInfo, ClassUtils.getUserClass(clazz).getName() + \" Not Found TableInfoCache.\");\n        return getGlobalConfig(tableInfo.getConfiguration()).getSqlSessionFactory();\n    }\n\n    /**\n     * 获取默认 MybatisGlobalConfig\n     */\n    public static GlobalConfig defaults() {\n        return new GlobalConfig().setDbConfig(new GlobalConfig.DbConfig());\n    }\n\n    /**\n     * <p>\n     * 设置全局设置(以configuration地址值作为Key)\n     * <p/>\n     *\n     * @param configuration Mybatis 容器配置对象\n     * @param globalConfig  全局配置\n     */\n    public static void setGlobalConfig(Configuration configuration, GlobalConfig globalConfig) {\n        Assert.isTrue(configuration != null && globalConfig != null, \"Error: Could not setGlobalConfig\");\n        // 设置全局设置\n        GLOBAL_CONFIG.putIfAbsent(Integer.toHexString(configuration.hashCode()), globalConfig);\n    }\n\n    /**\n     * 获取MybatisGlobalConfig (统一所有入口)\n     *\n     * @param configuration Mybatis 容器配置对象\n     */\n    public static GlobalConfig getGlobalConfig(Configuration configuration) {\n        Assert.notNull(configuration, \"Error: You need Initialize MybatisConfiguration !\");\n        final String key = Integer.toHexString(configuration.hashCode());\n        return CollectionUtils.computeIfAbsent(GLOBAL_CONFIG, key, k -> defaults());\n    }\n\n    public static List<IKeyGenerator> getKeyGenerators(Configuration configuration) {\n        return getGlobalConfig(configuration).getDbConfig().getKeyGenerators();\n    }\n\n    public static IdType getIdType(Configuration configuration) {\n        return getGlobalConfig(configuration).getDbConfig().getIdType();\n    }\n\n    public static GlobalConfig.DbConfig getDbConfig(Configuration configuration) {\n        return getGlobalConfig(configuration).getDbConfig();\n    }\n\n    public static ISqlInjector getSqlInjector(Configuration configuration) {\n        return getGlobalConfig(configuration).getSqlInjector();\n    }\n\n    public static Optional<MetaObjectHandler> getMetaObjectHandler(Configuration configuration) {\n        return Optional.ofNullable(getGlobalConfig(configuration).getMetaObjectHandler());\n    }\n\n    public static Optional<AnnotationHandler> getAnnotationHandler(Configuration configuration) {\n        return Optional.ofNullable(getGlobalConfig(configuration).getAnnotationHandler());\n    }\n\n    public static Class<?> getSuperMapperClass(Configuration configuration) {\n        return getGlobalConfig(configuration).getSuperMapperClass();\n    }\n\n    public static boolean isSupperMapperChildren(Configuration configuration, Class<?> mapperClass) {\n        return getSuperMapperClass(configuration).isAssignableFrom(mapperClass);\n    }\n\n    public static Set<String> getMapperRegistryCache(Configuration configuration) {\n        return getGlobalConfig(configuration).getMapperRegistryCache();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/IdWorker.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.UUID;\nimport java.util.concurrent.ThreadLocalRandom;\n\n/**\n * id 获取器\n *\n * @author hubin\n * @since 2016-08-01\n */\npublic class IdWorker {\n\n    /**\n     * 主机和进程的机器码\n     */\n    private static IdentifierGenerator IDENTIFIER_GENERATOR = entity -> DefaultIdentifierGenerator.getInstance().nextId(entity);\n\n    /**\n     * 毫秒格式化时间\n     */\n    public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern(\"yyyyMMddHHmmssSSS\");\n\n    /**\n     * 获取唯一ID\n     *\n     * @return id\n     */\n    public static long getId() {\n        return getId(null);\n    }\n\n    /**\n     * 获取唯一ID\n     *\n     * @return id\n     */\n    public static long getId(Object entity) {\n        return IDENTIFIER_GENERATOR.nextId(entity).longValue();\n    }\n\n    /**\n     * 获取唯一ID\n     *\n     * @return id\n     */\n    public static String getIdStr() {\n        return getIdStr(null);\n    }\n\n    /**\n     * 获取唯一ID\n     *\n     * @return id\n     */\n    public static String getIdStr(Object entity) {\n        return IDENTIFIER_GENERATOR.nextId(entity).toString();\n    }\n\n    /**\n     * 格式化的毫秒时间\n     */\n    public static String getMillisecond() {\n        return LocalDateTime.now().format(MILLISECOND);\n    }\n\n    /**\n     * 时间 ID = Time + ID\n     * <p>例如：可用于商品订单 ID</p>\n     */\n    public static String getTimeId() {\n        return getMillisecond() + getIdStr();\n    }\n\n    /**\n     * 有参构造器\n     *\n     * @param workerId     工作机器 ID\n     * @param dataCenterId 序列号\n     * @see #setIdentifierGenerator(IdentifierGenerator)\n     */\n    public static void initSequence(long workerId, long dataCenterId) {\n        IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator(workerId, dataCenterId);\n    }\n\n    /**\n     * 自定义id 生成方式\n     *\n     * @param identifierGenerator id 生成器\n     * @see GlobalConfig#setIdentifierGenerator(IdentifierGenerator)\n     */\n    public static void setIdentifierGenerator(IdentifierGenerator identifierGenerator) {\n        IDENTIFIER_GENERATOR = identifierGenerator;\n    }\n\n    /**\n     * 使用ThreadLocalRandom获取UUID获取更优的效果 去掉\"-\"\n     */\n    public static String get32UUID() {\n        ThreadLocalRandom random = ThreadLocalRandom.current();\n        return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/LambdaUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.support.*;\n\nimport java.lang.invoke.SerializedLambda;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static java.util.Locale.ENGLISH;\n\n/**\n * Lambda 解析工具类\n *\n * @author HCL, MieMie\n * @since 2018-05-10\n */\npublic final class LambdaUtils {\n\n    /**\n     * 字段映射\n     */\n    private static final Map<String, Map<String, ColumnCache>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();\n\n    /**\n     * 该缓存可能会在任意不定的时间被清除\n     *\n     * @param func 需要解析的 lambda 对象\n     * @param <T>  类型，被调用的 Function 对象的目标类型\n     * @return 返回解析后的结果\n     */\n    public static <T> LambdaMeta extract(SFunction<T, ?> func) {\n        // 1. IDEA 调试模式下 lambda 表达式是一个代理\n        if (func instanceof Proxy) {\n            return new IdeaProxyLambdaMeta((Proxy) func);\n        }\n        // 2. 反射读取\n        try {\n            Method method = func.getClass().getDeclaredMethod(\"writeReplace\");\n            method.setAccessible(true);\n            return new ReflectLambdaMeta((SerializedLambda) method.invoke(func), func.getClass().getClassLoader());\n        } catch (Throwable e) {\n            // 3. 反射失败使用序列化的方式读取\n            return new ShadowLambdaMeta(com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda.extract(func));\n        }\n    }\n\n    /**\n     * 格式化 key 将传入的 key 变更为大写格式\n     * 为了支持首字母是大写的字段\n     *\n     * <pre>\n     *     Assert.assertEquals(\"USERID\", formatKey(\"userId\"))\n     * </pre>\n     *\n     * @param key key\n     * @return 大写的 key\n     */\n    public static String formatKey(String key) {\n        return key.toUpperCase(ENGLISH);\n    }\n\n    /**\n     * 将传入的表信息加入缓存\n     *\n     * @param tableInfo 表信息\n     */\n    public static void installCache(TableInfo tableInfo) {\n        COLUMN_CACHE_MAP.put(tableInfo.getEntityType().getName(), createColumnCacheMap(tableInfo));\n    }\n\n    /**\n     * 缓存实体字段 MAP 信息\n     *\n     * @param info 表信息\n     * @return 缓存 map\n     */\n    private static Map<String, ColumnCache> createColumnCacheMap(TableInfo info) {\n        Map<String, ColumnCache> map;\n\n        if (info.havePK()) {\n            map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size() + 1);\n            map.put(formatKey(info.getKeyProperty()), new ColumnCache(info.getKeyColumn(), info.getKeySqlSelect()));\n        } else {\n            map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size());\n        }\n\n        info.getFieldList().forEach(i ->\n                map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect(), i.getMapping()))\n        );\n        return map;\n    }\n\n    /**\n     * 获取实体对应字段 MAP\n     *\n     * @param clazz 实体类\n     * @return 缓存 map\n     */\n    public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {\n        return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz.getName(), key -> {\n            TableInfo info = TableInfoHelper.getTableInfo(clazz);\n            return info == null ? null : createColumnCacheMap(info);\n        });\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/MybatisBatchUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.batch.BatchMethod;\nimport com.baomidou.mybatisplus.core.batch.BatchSqlSession;\nimport com.baomidou.mybatisplus.core.batch.MybatisBatch;\nimport com.baomidou.mybatisplus.core.batch.ParameterConvert;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.BiPredicate;\n\n/**\n * @author nieqiurong\n * @since 3.5.4\n */\npublic class MybatisBatchUtils {\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, String statement) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(statement);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, String statement, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(statement);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert  参数转换器\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, String statement, ParameterConvert<T> parameterConvert) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(statement, parameterConvert);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert  参数转换器\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, String statement, ParameterConvert<T> parameterConvert, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(statement, parameterConvert);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, String statement) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(autoCommit, statement);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, String statement, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(autoCommit, statement);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert  参数转换器\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, String statement, ParameterConvert<T> parameterConvert) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(autoCommit, statement, parameterConvert);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param statement         执行的 mapper 方法 (示例: com.baomidou.mybatisplus.core.mapper.BaseMapper.insert )\n     * @param parameterConvert  参数转换器\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, String statement, ParameterConvert<T> parameterConvert, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(autoCommit, statement, parameterConvert);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param batchMethod       批量操作方法\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, BatchMethod<T> batchMethod) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(batchMethod);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param batchMethod       批量操作方法\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, BatchMethod<T> batchMethod, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(batchMethod);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param batchMethod       批量操作方法\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, BatchMethod<T> batchMethod) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(autoCommit, batchMethod);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param batchMethod       批量操作方法\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> execute(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, BatchMethod<T> batchMethod, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).execute(autoCommit, batchMethod);\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param insertMethod      插入方法\n     * @param insertPredicate   插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod      更新方法\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> saveOrUpdate(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).saveOrUpdate(insertMethod, insertPredicate, updateMethod);\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param insertMethod      插入方法\n     * @param insertPredicate   插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod      更新方法\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> saveOrUpdate(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).saveOrUpdate(insertMethod, insertPredicate, updateMethod);\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param insertMethod      插入方法\n     * @param insertPredicate   插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod      更新方法\n     * @param <T>               泛型\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> saveOrUpdate(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList).saveOrUpdate(autoCommit, insertMethod, insertPredicate, updateMethod);\n    }\n\n    /**\n     * 批量保存或更新\n     * 这里需要注意一下,如果在insertPredicate里判断调用其他sqlSession(类似mapper.xxx)时,要注意一级缓存问题或数据感知问题(因为当前会话数据还未提交)\n     * 举个例子(事务开启状态下):\n     * 如果当前批次里面执行两个主键相同的数据,当调用mapper.selectById时,如果数据库未有这条记录,在同个sqlSession下,由于一级缓存的问题,下次再查就还是null,导致插入主键冲突,\n     * 但使用 {@link BatchSqlSession}时,由于每次select操作都会触发一次flushStatements,就会执行更新操作\n     *\n     * @param sqlSessionFactory sqlSessionFactory {@link SqlSessionFactory}\n     * @param dataList          数据集列表\n     * @param autoCommit        是否自动提交(这里生效的前提依赖于事务管理器 {@link org.apache.ibatis.transaction.Transaction})\n     * @param insertMethod      插入方法\n     * @param insertPredicate   插入条件 (当条件满足时执行插入方法,否则执行更新方法)\n     * @param updateMethod      更新方法\n     * @param <T>               泛型\n     * @param batchSize         插入批次数量\n     * @return 批处理结果\n     */\n    public static <T> List<BatchResult> saveOrUpdate(SqlSessionFactory sqlSessionFactory, Collection<T> dataList, boolean autoCommit, BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod, int batchSize) {\n        return new MybatisBatch<>(sqlSessionFactory, dataList, batchSize).saveOrUpdate(autoCommit, insertMethod, insertPredicate, updateMethod);\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/MybatisUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;\nimport com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata;\nimport com.baomidou.mybatisplus.core.override.MybatisMapperProxy;\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport lombok.experimental.UtilityClass;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.SystemMetaObject;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.defaults.DefaultSqlSession;\nimport org.apache.ibatis.type.TypeException;\nimport org.apache.ibatis.type.TypeHandler;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Proxy;\n\n/**\n * @author nieqiurong\n * @since 3.5.6\n */\n@Slf4j\n@UtilityClass\npublic class MybatisUtils {\n\n    /**\n     * 实例化Json类型处理器\n     * <p>\n     * 1.子类需要包含构造(Class,Field)\n     * 2.如果无上述构造或者无属性字段,则使用默认构造(Class)进行实例化\n     * </p>\n     *\n     * @param typeHandler   类型处理器 {@link IJsonTypeHandler}\n     * @param javaTypeClass java类型信息\n     * @param field         属性字段\n     * @return 实例化类型处理器\n     */\n    public static TypeHandler<?> newJsonTypeHandler(Class<? extends TypeHandler<?>> typeHandler, Class<?> javaTypeClass, Field field) {\n        TypeHandler<?> result = null;\n        if (IJsonTypeHandler.class.isAssignableFrom(typeHandler)) {\n            if (field != null) {\n                try {\n                    result = typeHandler.getConstructor(Class.class, Field.class).newInstance(javaTypeClass, field);\n                } catch (ReflectiveOperationException e) {\n                    // ignore\n                }\n            }\n            if (result == null) {\n                try {\n                    result = typeHandler.getConstructor(Class.class).newInstance(javaTypeClass);\n                } catch (ReflectiveOperationException ex) {\n                    throw new TypeException(\"Failed invoking constructor for handler \" + typeHandler, ex);\n                }\n            }\n        }\n        return result;\n    }\n\n    /**\n     * 获取SqlSessionFactory\n     * <p>当自定义实现{@link SqlSession}时,请实现对{@link SqlSessionFactory}的访问 (spring的方式)</p>\n     * <p>当无法获得{@link SqlSessionFactory}时,需要将{@link SqlSessionFactory}绑定至上下文对象中(原生mybatis访问方式)</p>\n     *\n     * @param mybatisMapperProxy {@link MybatisMapperProxy}\n     * @return SqlSessionFactory\n     * @see DefaultSqlSession\n     * @see GlobalConfigUtils#getGlobalConfig(Configuration)\n     * @see GlobalConfigUtils#setGlobalConfig(Configuration, GlobalConfig)\n     * @since 3.5.7\n     */\n    public static SqlSessionFactory getSqlSessionFactory(MybatisMapperProxy<?> mybatisMapperProxy) {\n        SqlSession sqlSession = mybatisMapperProxy.getSqlSession();\n        return getSqlSessionFactory(sqlSession);\n    }\n\n    /**\n     * 获取sqlSession中的SqlSessionFactory\n     *\n     * @param sqlSession sqlSession会话\n     * @return SqlSessionFactory\n     * @since 3.5.12\n     */\n    public static SqlSessionFactory getSqlSessionFactory(SqlSession sqlSession) {\n        MetaObject metaObject = SystemMetaObject.forObject(sqlSession);\n        String property = \"sqlSessionFactory\";\n        if (metaObject.hasGetter(property)) {\n            return (SqlSessionFactory) metaObject.getValue(property);\n        }\n        SqlSessionFactory sqlSessionFactory = GlobalConfigUtils.getGlobalConfig(sqlSession.getConfiguration()).getSqlSessionFactory();\n        Assert.isTrue(sqlSessionFactory != null, \"Please implement access to the sqlSessionFactory property or bind sqlSessionFactory to global access.\");\n        return sqlSessionFactory;\n    }\n\n    /**\n     * 获取代理实现\n     *\n     * @param mapper mapper类\n     * @return 代理实现\n     * @since 3.5.7\n     */\n    public static MybatisMapperProxy<?> getMybatisMapperProxy(Object mapper) {\n        Object result = extractMapperProxy(mapper);\n        if (result instanceof MybatisMapperProxy) {\n            return (MybatisMapperProxy<?>) result;\n        }\n        throw new MybatisPlusException(\"Unable to get MybatisMapperProxy : \" + mapper);\n    }\n\n    /**\n     * 提取MapperProxy\n     *\n     * @param mapper Mapper对象\n     * @return 真实Mapper对象(去除动态代理增强)\n     * @since 3.5.12\n     */\n    public static Object extractMapperProxy(Object mapper) {\n        if (mapper instanceof MybatisMapperProxy) {\n            // fast return\n            return mapper;\n        }\n        Object result = mapper;\n        if (CompatibleHelper.hasCompatibleSet()) {\n            Object proxyTargetObject = CompatibleHelper.getCompatibleSet().getProxyTargetObject(result);\n            if (proxyTargetObject != null) {\n                result = proxyTargetObject;\n            }\n        }\n        if (result != null) {\n            while (Proxy.isProxyClass(result.getClass())) {\n                result = Proxy.getInvocationHandler(result);\n            }\n        }\n        return result;\n    }\n\n\n    /**\n     * 获取MapperProxy元数据信息\n     *\n     * @param mapper Mapper对象\n     * @return 代理属性\n     * @since 3.5.12\n     */\n    public static MapperProxyMetadata getMapperProxy(Object mapper) {\n        Object mapperProxy = extractMapperProxy(mapper);\n        MetaObject metaObject = SystemMetaObject.forObject(mapperProxy);\n        return new MapperProxyMetadata(metaObject);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/NetUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.io.IOException;\nimport java.net.Inet4Address;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.UnknownHostException;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\n\n/**\n * 来源 SpringCloud-Commons {@link org.springframework.cloud.commons.util.InetUtils}.\n *\n * @author nieqiurong 2024年4月13日 20:49:47\n * @since 3.5.7\n */\npublic class NetUtils {\n\n    private static final Log LOG = LogFactory.getLog(NetUtils.class);\n\n    private final NetProperties netProperties;\n\n    public NetUtils(NetProperties netProperties) {\n        this.netProperties = netProperties;\n    }\n\n    public InetAddress findFirstNonLoopbackAddress() {\n        InetAddress result = null;\n        try {\n            for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();\n                 networkInterfaces.hasMoreElements(); ) {\n                NetworkInterface networkInterface = networkInterfaces.nextElement();\n                if (networkInterface.isUp()) {\n                    if (!ignoreInterface(networkInterface.getDisplayName())) {\n                        for (Enumeration<InetAddress> addresses = networkInterface\n                            .getInetAddresses(); addresses.hasMoreElements(); ) {\n                            InetAddress address = addresses.nextElement();\n                            if (address instanceof Inet4Address\n                                && !address.isLoopbackAddress()\n                                && isPreferredAddress(address)) {\n                                result = address;\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (IOException exception) {\n            LOG.error(\"Cannot get first non-loopback address\", exception);\n        }\n        if (result != null) {\n            return result;\n        }\n        try {\n            return InetAddress.getLocalHost();\n        } catch (UnknownHostException e) {\n            throw new RuntimeException(\"Unable to retrieve localhost\");\n        }\n    }\n\n    boolean isPreferredAddress(InetAddress address) {\n        final List<String> preferredNetworks = this.netProperties.getPreferredNetworks();\n        if (preferredNetworks.isEmpty()) {\n            return true;\n        }\n        for (String regex : preferredNetworks) {\n            final String hostAddress = address.getHostAddress();\n            if (hostAddress.matches(regex) || hostAddress.startsWith(regex)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    boolean ignoreInterface(String interfaceName) {\n        List<String> ignoredInterfaces = this.netProperties.getIgnoredInterfaces();\n        for (String regex : ignoredInterfaces) {\n            if (interfaceName.matches(regex)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    public static class NetProperties {\n\n        /**\n         * 首选网络地址 (例如: 192.168.1,支持正则)\n         */\n        private List<String> preferredNetworks = new ArrayList<>();\n\n        /**\n         * 忽略网卡(例如:eth0,,支持正则)\n         */\n        private List<String> ignoredInterfaces = new ArrayList<>();\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ObjectUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.lang.reflect.Array;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * 对象工具类\n *\n * @author hubin\n * @since 2018-06-05\n */\npublic class ObjectUtils {\n\n    /**\n     * 判断object是否为空,集合会校验size\n     */\n    public static boolean isNull(Object... objs) {\n        for (Object obj : objs) {\n            if (isEmpty(obj)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断object是否不为空,集合会校验size\n     */\n    public static boolean isNotNull(Object... obj) {\n        return !isNull(obj);\n    }\n\n    /**\n     * 对象非空判断\n     */\n    public static boolean isNotEmpty(Object obj) {\n        return !isEmpty(obj);\n    }\n\n    /**\n     * 对象空判断\n     */\n    public static boolean isEmpty(Object obj) {\n        if (obj == null) {\n            return true;\n        }\n\n        if (obj.getClass().isArray()) {\n            return Array.getLength(obj) == 0;\n        }\n        if (obj instanceof CharSequence) {\n            return ((CharSequence) obj).length() == 0;\n        }\n        if (obj instanceof Collection) {\n            return ((Collection<?>) obj).isEmpty();\n        }\n        if (obj instanceof Map) {\n            return ((Map<?, ?>) obj).isEmpty();\n        }\n        if (obj instanceof Iterable) {\n            return !((Iterable<?>) obj).iterator().hasNext();\n        }\n        if (obj instanceof Iterator) {\n            return !((Iterator<?>) obj).hasNext();\n        }\n        // else\n        return false;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ParameterUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\n\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * 参数工具类\n *\n * @author nieqiuqiu\n * @since 2020-01-10\n * @since 3.3.1\n */\npublic class ParameterUtils {\n\n    private ParameterUtils() {\n\n    }\n\n    /**\n     * 查找分页参数\n     *\n     * @param parameterObject 参数对象\n     * @return 分页参数\n     */\n    public static Optional<IPage> findPage(Object parameterObject) {\n        if (parameterObject != null) {\n            if (parameterObject instanceof Map) {\n                Map<?, ?> parameterMap = (Map<?, ?>) parameterObject;\n                for (Map.Entry entry : parameterMap.entrySet()) {\n                    if (entry.getValue() != null && entry.getValue() instanceof IPage) {\n                        return Optional.of((IPage) entry.getValue());\n                    }\n                }\n            } else if (parameterObject instanceof IPage) {\n                return Optional.of((IPage) parameterObject);\n            }\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/PluginUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.plugin.Plugin;\nimport org.apache.ibatis.reflection.DefaultReflectorFactory;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.SystemMetaObject;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.lang.reflect.Proxy;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 插件工具类\n *\n * @author TaoYu , hubin\n * @since 2017-06-20\n */\npublic abstract class PluginUtils {\n\n    /**\n     * 缓存内置的插件对象反射信息\n     *\n     * @since 3.5.3.2\n     */\n    public static final DefaultReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();\n\n    public static final String DELEGATE_BOUNDSQL_SQL = \"delegate.boundSql.sql\";\n\n    /**\n     * 获得真正的处理对象,可能多层代理.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T realTarget(Object target) {\n        if (Proxy.isProxyClass(target.getClass())) {\n            Plugin plugin = (Plugin) Proxy.getInvocationHandler(target);\n            MetaObject metaObject = getMetaObject(plugin);\n            return realTarget(metaObject.getValue(\"target\"));\n        }\n        return (T) target;\n    }\n\n    /**\n     * 获取对象元数据信息\n     *\n     * @param object 参数\n     * @return 元数据信息\n     * @since 3.5.3\n     */\n    public static MetaObject getMetaObject(Object object) {\n        return MetaObject.forObject(object, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);\n    }\n\n    /**\n     * 给 BoundSql 设置 additionalParameters\n     *\n     * @param boundSql             BoundSql\n     * @param additionalParameters additionalParameters\n     */\n    public static void setAdditionalParameter(BoundSql boundSql, Map<String, Object> additionalParameters) {\n        additionalParameters.forEach(boundSql::setAdditionalParameter);\n    }\n\n    public static MPBoundSql mpBoundSql(BoundSql boundSql) {\n        return new MPBoundSql(boundSql);\n    }\n\n    public static MPStatementHandler mpStatementHandler(StatementHandler statementHandler) {\n        statementHandler = realTarget(statementHandler);\n        MetaObject object = getMetaObject(statementHandler);\n        return new MPStatementHandler(getMetaObject(object.getValue(\"delegate\")));\n    }\n\n    /**\n     * {@link org.apache.ibatis.executor.statement.BaseStatementHandler}\n     */\n    public static class MPStatementHandler {\n        private final MetaObject statementHandler;\n\n        MPStatementHandler(MetaObject statementHandler) {\n            this.statementHandler = statementHandler;\n        }\n\n        public ParameterHandler parameterHandler() {\n            return get(\"parameterHandler\");\n        }\n\n        public MappedStatement mappedStatement() {\n            return get(\"mappedStatement\");\n        }\n\n        public Executor executor() {\n            return get(\"executor\");\n        }\n\n        public MPBoundSql mPBoundSql() {\n            return new MPBoundSql(boundSql());\n        }\n\n        public BoundSql boundSql() {\n            return get(\"boundSql\");\n        }\n\n        public Configuration configuration() {\n            return get(\"configuration\");\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        private <T> T get(String property) {\n            return (T) statementHandler.getValue(property);\n        }\n    }\n\n    /**\n     * {@link BoundSql}\n     */\n    public static class MPBoundSql {\n\n        private final MetaObject boundSql;\n        private final BoundSql delegate;\n\n        MPBoundSql(BoundSql boundSql) {\n            this.delegate = boundSql;\n            this.boundSql = getMetaObject(boundSql);\n        }\n\n        public String sql() {\n            return delegate.getSql();\n        }\n\n        public void sql(String sql) {\n            boundSql.setValue(\"sql\", sql);\n        }\n\n        public List<ParameterMapping> parameterMappings() {\n            List<ParameterMapping> parameterMappings = delegate.getParameterMappings();\n            return new ArrayList<>(parameterMappings);\n        }\n\n        public void parameterMappings(List<ParameterMapping> parameterMappings) {\n            boundSql.setValue(\"parameterMappings\", Collections.unmodifiableList(parameterMappings));\n        }\n\n        public Object parameterObject() {\n            return get(\"parameterObject\");\n        }\n\n        public Map<String, Object> additionalParameters() {\n            return get(\"additionalParameters\");\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        private <T> T get(String property) {\n            return (T) boundSql.getValue(property);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/ReflectionKit.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;\nimport com.baomidou.mybatisplus.core.toolkit.reflect.TypeParameterResolver;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.security.AccessController;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static java.util.function.Function.identity;\nimport static java.util.stream.Collectors.toMap;\n\n/**\n * 反射工具类，提供反射相关的快捷操作\n *\n * @author Caratacus\n * @author hcl\n * @since 2016-09-22\n */\npublic final class ReflectionKit {\n    /**\n     * class field cache\n     */\n    private static final Map<Class<?>, List<Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();\n\n    @Deprecated\n    private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_TYPE_MAP = new IdentityHashMap<>(8);\n\n    private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_TO_WRAPPER_MAP = new IdentityHashMap<>(8);\n\n    static {\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class);\n        PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class);\n        for (Map.Entry<Class<?>, Class<?>> entry : PRIMITIVE_WRAPPER_TYPE_MAP.entrySet()) {\n            PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(entry.getValue(), entry.getKey());\n        }\n    }\n\n    /**\n     * 获取字段值\n     *\n     * @param entity    实体\n     * @param fieldName 字段名称\n     * @return 属性值\n     * @deprecated 3.5.4\n     */\n    @Deprecated\n    public static Object getFieldValue(Object entity, String fieldName) {\n        Class<?> cls = entity.getClass();\n        Map<String, Field> fieldMaps = getFieldMap(cls);\n        try {\n            Field field = fieldMaps.get(fieldName);\n            Assert.notNull(field, \"Error: NoSuchField in %s for %s.  Cause:\", cls.getSimpleName(), fieldName);\n            field.setAccessible(true);\n            return field.get(entity);\n        } catch (ReflectiveOperationException e) {\n            throw ExceptionUtils.mpe(\"Error: Cannot read field in %s.  Cause:\", e, cls.getSimpleName());\n        }\n    }\n\n    /**\n     * <p>\n     * 反射对象获取泛型\n     * </p>\n     *\n     * @param clazz      对象\n     * @param genericIfc 所属泛型父类\n     * @param index      泛型所在位置\n     * @return Class\n     */\n    public static Class<?> getSuperClassGenericType(final Class<?> clazz, final Class<?> genericIfc, final int index) {\n        // 这里泛型逻辑提取进行了调整,如果在Spring项目情况或者自定义了泛型提取,那就优先走这里,否则使用框架内置的进行泛型提取.\n        Class<?> userClass = ClassUtils.getUserClass(clazz);\n        if (GenericTypeUtils.hasGenericTypeResolver()) {\n            Class<?>[] typeArguments = GenericTypeUtils.resolveTypeArguments(userClass, genericIfc);\n            return null == typeArguments ? null : typeArguments[index];\n        }\n        return (Class<?>) TypeParameterResolver.resolveClassIndexedParameter(userClass, genericIfc, index);\n    }\n\n    /**\n     * <p>\n     * 获取该类的所有属性列表\n     * </p>\n     *\n     * @param clazz 反射类\n     */\n    public static Map<String, Field> getFieldMap(Class<?> clazz) {\n        List<Field> fieldList = getFieldList(clazz);\n        return CollectionUtils.isNotEmpty(fieldList) ? fieldList.stream().collect(Collectors.toMap(Field::getName, Function.identity())) : Collections.emptyMap();\n    }\n\n    /**\n     * <p>\n     * 获取该类的所有属性列表\n     * </p>\n     *\n     * @param clazz 反射类\n     */\n    public static List<Field> getFieldList(Class<?> clazz) {\n        if (Objects.isNull(clazz)) {\n            return Collections.emptyList();\n        }\n        return CollectionUtils.computeIfAbsent(CLASS_FIELD_CACHE, clazz, k -> {\n            Field[] fields = k.getDeclaredFields();\n            List<Field> superFields = new ArrayList<>();\n            Class<?> currentClass = k.getSuperclass();\n            while (currentClass != null) {\n                Field[] declaredFields = currentClass.getDeclaredFields();\n                Collections.addAll(superFields, declaredFields);\n                currentClass = currentClass.getSuperclass();\n            }\n            /* 排除重载属性 */\n            Map<String, Field> fieldMap = excludeOverrideSuperField(fields, superFields);\n            /*\n             * 重写父类属性过滤后处理忽略部分，支持过滤父类属性功能\n             * 场景：中间表不需要记录创建时间，忽略父类 createTime 公共属性\n             * 中间表实体重写父类属性 ` private transient Date createTime; `\n             */\n            return fieldMap.values().stream()\n                /* 过滤静态属性 */\n                .filter(f -> !Modifier.isStatic(f.getModifiers()))\n                /* 过滤 transient关键字修饰的属性 */\n                .filter(f -> !Modifier.isTransient(f.getModifiers()))\n                .collect(Collectors.toList());\n        });\n    }\n\n    /**\n     * <p>\n     * 排序重置父类属性\n     * </p>\n     *\n     * @param fields         子类属性\n     * @param superFieldList 父类属性\n     */\n    public static Map<String, Field> excludeOverrideSuperField(Field[] fields, List<Field> superFieldList) {\n        // 子类属性\n        Map<String, Field> fieldMap = Stream.of(fields).collect(toMap(Field::getName, identity(),\n            (u, v) -> {\n                throw new IllegalStateException(String.format(\"Duplicate key %s\", u));\n            },\n            LinkedHashMap::new));\n        superFieldList.stream().filter(field -> !fieldMap.containsKey(field.getName()))\n            .forEach(f -> fieldMap.put(f.getName(), f));\n        return fieldMap;\n    }\n\n    /**\n     * 判断是否为基本类型或基本包装类型\n     *\n     * @param clazz class\n     * @return 是否基本类型或基本包装类型\n     */\n    @Deprecated\n    public static boolean isPrimitiveOrWrapper(Class<?> clazz) {\n        Assert.notNull(clazz, \"Class must not be null\");\n        return (clazz.isPrimitive() || PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(clazz));\n    }\n\n    public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) {\n        return (clazz.isPrimitive() && clazz != void.class ? PRIMITIVE_TYPE_TO_WRAPPER_MAP.get(clazz) : clazz);\n    }\n\n    /**\n     * 设置可访问对象的可访问权限为 true\n     *\n     * @param object 可访问的对象\n     * @param <T>    类型\n     * @return 返回设置后的对象\n     * @deprecated 3.5.4 {@link java.security.AccessController}\n     */\n    @Deprecated\n    public static <T extends AccessibleObject> T setAccessible(T object) {\n        return AccessController.doPrivileged(new SetAccessibleAction<>(object));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/Sequence.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.lang.management.ManagementFactory;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 分布式高效有序 ID 生产黑科技(sequence)\n *\n * <p>优化开源项目：<a href=\"https://gitee.com/yu120/sequence\">sequence</a></p>\n *\n * @author hubin\n * @since 2016-08-18\n */\npublic class Sequence {\n\n    /**\n     * 自动寻找网卡时,默认启动最大时间间隔,超过这个初始化时间打印warn日志\n     *\n     * @since 3.5.6\n     */\n    public static long MAX_START_INTERVAL_TIME = TimeUnit.SECONDS.toNanos(5);\n\n    private static final Log logger = LogFactory.getLog(Sequence.class);\n    /**\n     * 时间起始标记点，作为基准，一般取系统的最近时间（一旦确定不能变动）\n     */\n    private static final long twepoch = 1288834974657L;\n    /**\n     * 机器标识位数\n     */\n    private final long workerIdBits = 5L;\n    private final long datacenterIdBits = 5L;\n    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);\n    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);\n    /**\n     * 毫秒内自增位\n     */\n    private final long sequenceBits = 12L;\n    private final long workerIdShift = sequenceBits;\n    private final long datacenterIdShift = sequenceBits + workerIdBits;\n    /**\n     * 时间戳左移动位\n     */\n    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;\n    private final long sequenceMask = -1L ^ (-1L << sequenceBits);\n\n    private final long workerId;\n\n    /**\n     * 数据标识 ID 部分\n     */\n    private final long datacenterId;\n    /**\n     * 并发控制\n     */\n    private long sequence = 0L;\n    /**\n     * 上次生产 ID 时间戳\n     */\n    private long lastTimestamp = -1L;\n    /**\n     * IP 地址\n     */\n    private InetAddress inetAddress;\n\n    public Sequence(InetAddress inetAddress) {\n        this.inetAddress = inetAddress;\n        long start = System.nanoTime();\n        this.datacenterId = getDatacenterId(maxDatacenterId);\n        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);\n        long end = System.nanoTime();\n        if (end - start > Sequence.MAX_START_INTERVAL_TIME) {\n            // 一般这里启动慢,是未指定inetAddress时出现,请查看本机hostname,将本机hostname写入至本地系统hosts文件之中进行解析\n            logger.warn(\"Initialization Sequence Very Slow! Get datacenterId:\" + this.datacenterId + \" workerId:\" + this.workerId);\n        } else {\n            initLog();\n        }\n    }\n\n    private void initLog() {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"Initialization Sequence datacenterId:\" + this.datacenterId + \" workerId:\" + this.workerId);\n        }\n    }\n\n    /**\n     * 有参构造器\n     *\n     * @param workerId     工作机器 ID\n     * @param datacenterId 序列号\n     */\n    public Sequence(long workerId, long datacenterId) {\n        Assert.isFalse(workerId > maxWorkerId || workerId < 0,\n            String.format(\"worker Id can't be greater than %d or less than 0\", maxWorkerId));\n        Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,\n            String.format(\"datacenter Id can't be greater than %d or less than 0\", maxDatacenterId));\n        this.workerId = workerId;\n        this.datacenterId = datacenterId;\n        initLog();\n    }\n\n    /**\n     * 反解id的时间戳部分\n     */\n    public static long parseIdTimestamp(long id) {\n        return (id >> 22) + twepoch;\n    }\n\n    /**\n     * 数据标识id部分\n     */\n    protected long getDatacenterId(long maxDatacenterId) {\n        long id = 0L;\n        try {\n            if (null == this.inetAddress) {\n                if (logger.isDebugEnabled()) {\n                    logger.debug(\"Use localhost address \");\n                }\n                this.inetAddress = InetAddress.getLocalHost();\n            }\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Get \" + inetAddress + \" network interface \");\n            }\n            NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Get network interface info: \" + network);\n            }\n            if (null == network) {\n                logger.warn(\"Unable to get network interface for \" + inetAddress);\n                id = 1L;\n            } else {\n                byte[] mac = network.getHardwareAddress();\n                if (null != mac) {\n                    id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;\n                    id = id % (maxDatacenterId + 1);\n                }\n            }\n        } catch (Exception e) {\n            logger.warn(\" getDatacenterId: \" + e.getMessage());\n        }\n        return id;\n    }\n\n    /**\n     * 获取下一个 ID\n     *\n     * @return 下一个 ID\n     */\n    public synchronized long nextId() {\n        long timestamp = timeGen();\n        //闰秒\n        if (timestamp < lastTimestamp) {\n            long offset = lastTimestamp - timestamp;\n            if (offset <= 5) {\n                try {\n                    wait(offset << 1);\n                    timestamp = timeGen();\n                    if (timestamp < lastTimestamp) {\n                        throw new RuntimeException(String.format(\"Clock moved backwards.  Refusing to generate id for %d milliseconds\", offset));\n                    }\n                } catch (Exception e) {\n                    throw new RuntimeException(e);\n                }\n            } else {\n                throw new RuntimeException(String.format(\"Clock moved backwards.  Refusing to generate id for %d milliseconds\", offset));\n            }\n        }\n\n        if (lastTimestamp == timestamp) {\n            // 相同毫秒内，序列号自增\n            sequence = (sequence + 1) & sequenceMask;\n            if (sequence == 0) {\n                // 同一毫秒的序列数已经达到最大\n                timestamp = tilNextMillis(lastTimestamp);\n            }\n        } else {\n            // 不同毫秒内，序列号置为 1 - 2 随机数\n            sequence = ThreadLocalRandom.current().nextLong(1, 3);\n        }\n\n        lastTimestamp = timestamp;\n\n        // 时间戳部分 | 数据中心部分 | 机器标识部分 | 序列号部分\n        return ((timestamp - twepoch) << timestampLeftShift)\n            | (datacenterId << datacenterIdShift)\n            | (workerId << workerIdShift)\n            | sequence;\n    }\n\n    protected long tilNextMillis(long lastTimestamp) {\n        long timestamp = timeGen();\n        while (timestamp <= lastTimestamp) {\n            timestamp = timeGen();\n        }\n        return timestamp;\n    }\n\n    protected long timeGen() {\n        return SystemClock.now();\n    }\n\n    /**\n     * 获取 maxWorkerId\n     */\n    protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {\n        StringBuilder mpid = new StringBuilder();\n        mpid.append(datacenterId);\n        String name = ManagementFactory.getRuntimeMXBean().getName();\n        if (StringUtils.isNotBlank(name)) {\n            /*\n             * GET jvmPid\n             */\n            int pid = Integer.parseInt(name.split(StringPool.AT)[0]);\n            if (pid < 10) { // 疑似容器环境\n                pid = ThreadLocalRandom.current().nextInt(10, 4194304);\n            }\n            mpid.append(pid);\n        }\n        /*\n         * MAC + PID 的 hashcode 获取16个低位\n         */\n        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/SerializationUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.io.*;\n\n\n/**\n * <p> copy from spring-core#org.springframework.util.SerializationUtils version 5.2.8 </p>\n *\n * @since 1.0\n */\npublic class SerializationUtils {\n\n    /**\n     * Deep clone an {@code Object} using serialization.\n     * <p>This is many times slower than writing clone methods by hand\n     * on all objects in your object graph. However, for complex object\n     * graphs, or for those that don't support deep cloning this can\n     * be a simple alternative implementation. Of course all the objects\n     * must be {@code Serializable}.</p>\n     *\n     * @param <T>    the type of the object involved\n     * @param object the {@code Serializable} object to clone\n     * @return the cloned object\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends Serializable> T clone(final T object) {\n        if (object == null) {\n            return null;\n        }\n        final byte[] objectData = serialize(object);\n        return (T) deserialize(objectData);\n    }\n\n    /**\n     * Serialize the given object to a byte array.\n     *\n     * @param object the object to serialize\n     * @return an array of bytes representing the object in a portable fashion\n     */\n    public static byte[] serialize(Object object) {\n        if (object == null) {\n            return null;\n        }\n        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);\n        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {\n            oos.writeObject(object);\n            oos.flush();\n        } catch (IOException ex) {\n            throw new IllegalArgumentException(\"Failed to serialize object of type: \" + object.getClass(), ex);\n        }\n        return baos.toByteArray();\n    }\n\n    /**\n     * Deserialize the byte array into an object.\n     *\n     * @param bytes a serialized object\n     * @return the result of deserializing the bytes\n     */\n    public static Object deserialize(byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        }\n        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {\n            return ois.readObject();\n        } catch (IOException ex) {\n            throw new IllegalArgumentException(\"Failed to deserialize object\", ex);\n        } catch (ClassNotFoundException ex) {\n            throw new IllegalStateException(\"Failed to deserialize object type\", ex);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/SetAccessibleAction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.security.PrivilegedAction;\n\n/**\n * Create by hcl at 2021/5/14\n *\n * @deprecated 3.5.4\n */\n@Deprecated\npublic class SetAccessibleAction<T extends AccessibleObject> implements PrivilegedAction<T> {\n    private final T obj;\n\n    public SetAccessibleAction(T obj) {\n        this.obj = obj;\n    }\n\n    @Override\n    public T run() {\n        obj.setAccessible(true);\n        return obj;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/StringPool.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\n/**\n * Copy to jodd.util\n * <p>\n * Pool of <code>String</code> constants to prevent repeating of\n * hard-coded <code>String</code> literals in the code.\n * Due to fact that these are <code>public static final</code>\n * they will be inlined by java compiler and\n * reference to this class will be dropped.\n * There is <b>no</b> performance gain of using this pool.\n * Read: <a href=\"https://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5\">Java SE Specifications</a>\n * <ul>\n * <li>Literal strings within the same class in the same package represent references to the same <code>String</code> object.</li>\n * <li>Literal strings within different classes in the same package represent references to the same <code>String</code> object.</li>\n * <li>Literal strings within different classes in different packages likewise represent references to the same <code>String</code> object.</li>\n * <li>Strings computed by constant expressions are computed at compile time and then treated as if they were literals.</li>\n * <li>Strings computed by concatenation at run time are newly created and therefore distinct.</li>\n * </ul>\n */\npublic interface StringPool {\n\n    String AMPERSAND = \"&\";\n    String AND = \"and\";\n    String AT = \"@\";\n    String ASTERISK = \"*\";\n    String STAR = ASTERISK;\n    String BACK_SLASH = \"\\\\\";\n    String COLON = \":\";\n    String COMMA = \",\";\n    String DASH = \"-\";\n    String DOLLAR = \"$\";\n    String DOT = \".\";\n    String DOTDOT = \"..\";\n    String DOT_CLASS = \".class\";\n    String DOT_JAVA = \".java\";\n    String DOT_XML = \".xml\";\n    String EMPTY = \"\";\n    String EQUALS = \"=\";\n    String FALSE = \"false\";\n    String SLASH = \"/\";\n    String HASH = \"#\";\n    String HAT = \"^\";\n    String LEFT_BRACE = \"{\";\n    String LEFT_BRACKET = \"(\";\n    String LEFT_CHEV = \"<\";\n    String DOT_NEWLINE = \",\\n\";\n    String NEWLINE = \"\\n\";\n    String N = \"n\";\n    String NO = \"no\";\n    String NULL = \"null\";\n    String NUM = \"NUM\";\n    String OFF = \"off\";\n    String ON = \"on\";\n    String PERCENT = \"%\";\n    String PIPE = \"|\";\n    String PLUS = \"+\";\n    String QUESTION_MARK = \"?\";\n    String EXCLAMATION_MARK = \"!\";\n    String QUOTE = \"\\\"\";\n    String RETURN = \"\\r\";\n    String TAB = \"\\t\";\n    String RIGHT_BRACE = \"}\";\n    String RIGHT_BRACKET = \")\";\n    String RIGHT_CHEV = \">\";\n    String SEMICOLON = \";\";\n    String SINGLE_QUOTE = \"'\";\n    String BACKTICK = \"`\";\n    String SPACE = \" \";\n    String SQL = \"sql\";\n    String TILDA = \"~\";\n    String LEFT_SQ_BRACKET = \"[\";\n    String RIGHT_SQ_BRACKET = \"]\";\n    String TRUE = \"true\";\n    String UNDERSCORE = \"_\";\n    String UTF_8 = \"UTF-8\";\n    String US_ASCII = \"US-ASCII\";\n    String ISO_8859_1 = \"ISO-8859-1\";\n    String Y = \"y\";\n    String YES = \"yes\";\n    String ONE = \"1\";\n    String ZERO = \"0\";\n    String DOLLAR_LEFT_BRACE = \"${\";\n    String HASH_LEFT_BRACE = \"#{\";\n    String CRLF = \"\\r\\n\";\n\n    String HTML_NBSP = \"&nbsp;\";\n    String HTML_AMP = \"&amp\";\n    String HTML_QUOTE = \"&quot;\";\n    String HTML_LT = \"&lt;\";\n    String HTML_GT = \"&gt;\";\n\n    // ---------------------------------------------------------------- array\n\n    String[] EMPTY_ARRAY = new String[0];\n\n    byte[] BYTES_NEW_LINE = StringPool.NEWLINE.getBytes();\n\n    // SQL method\n    String SELECT_WITH_CURSOR = \"selectWithCursor\";\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/StringUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;\nimport com.baomidou.mybatisplus.core.toolkit.support.BiIntFunction;\n\nimport java.util.Collection;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * String 工具类\n *\n * @author D.Yang, hcl\n * @author hcl\n * @since 2016-08-18\n */\npublic final class StringUtils {\n\n    /**\n     * 字符串 is\n     */\n    public static final String IS = \"is\";\n    /**\n     * 下划线字符\n     */\n    public static final char UNDERLINE = '_';\n    /**\n     * MP 内定义的 SQL 占位符表达式，匹配诸如 {0},{1},{2} ... 的形式\n     */\n    public final static Pattern MP_SQL_PLACE_HOLDER = Pattern.compile(\"[{](?<idx>\\\\d+)}\");\n    /**\n     * 验证字符串是否是数据库字段\n     */\n    private static final Pattern P_IS_COLUMN = Pattern.compile(\"^\\\\w\\\\S*[\\\\w\\\\d]*$\");\n\n    /**\n     * 是否为大写命名\n     */\n    private static final Pattern CAPITAL_MODE = Pattern.compile(\"^[0-9A-Z/_]+$\");\n\n    /**\n     * 字符串去除空白内容\n     *\n     * <ul> <li>'\"<>&*+=#-; sql注入黑名单</li> <li>\\n 回车</li> <li>\\t 水平制表符</li> <li>\\s 空格</li> <li>\\r 换行</li> </ul>\n     */\n    private static final Pattern REPLACE_BLANK = Pattern.compile(\"'|\\\"|\\\\<|\\\\>|&|\\\\*|\\\\+|=|#|-|;|\\\\s*|\\t|\\r|\\n\");\n\n    /**\n     * 判断字符串中是否全是空白字符\n     *\n     * @param cs 需要判断的字符串\n     * @return 如果字符串序列是 null 或者全是空白，返回 true\n     */\n    public static boolean isBlank(CharSequence cs) {\n        if (cs != null) {\n            int length = cs.length();\n            for (int i = 0; i < length; i++) {\n                if (!Character.isWhitespace(cs.charAt(i))) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 对象转为字符串去除左右空格\n     *\n     * @param o 带转换对象\n     * @return\n     */\n    public static String toStringTrim(Object o) {\n        return String.valueOf(o).trim();\n    }\n\n    /**\n     * @see #isBlank(CharSequence)\n     */\n    public static boolean isNotBlank(CharSequence cs) {\n        return !isBlank(cs);\n    }\n\n    public static boolean isEmpty(CharSequence cs) {\n        return cs == null || cs.length() == 0;\n    }\n\n    public static boolean isNotEmpty(CharSequence cs) {\n        return !isEmpty(cs);\n    }\n\n    /**\n     * 判断字符串是不是驼峰命名\n     *\n     * <li> 包含 '_' 不算 </li>\n     * <li> 首字母大写的不算 </li>\n     *\n     * @param str 字符串\n     * @return 结果\n     */\n    public static boolean isCamel(String str) {\n        return Character.isLowerCase(str.charAt(0)) && !str.contains(StringPool.UNDERSCORE);\n    }\n\n    /**\n     * 判断字符串是否符合数据库字段的命名\n     *\n     * @param str 字符串\n     * @return 判断结果\n     */\n    public static boolean isNotColumnName(String str) {\n        return !P_IS_COLUMN.matcher(str).matches();\n    }\n\n    /**\n     * 获取真正的字段名\n     *\n     * @param column 字段名\n     * @return 字段名\n     */\n    public static String getTargetColumn(String column) {\n        if (isNotColumnName(column)) {\n            return column.substring(1, column.length() - 1);\n        }\n        return column;\n    }\n\n    /**\n     * 字符串驼峰转下划线格式\n     *\n     * @param param 需要转换的字符串\n     * @return 转换好的字符串\n     */\n    public static String camelToUnderline(String param) {\n        if (isBlank(param)) {\n            return StringPool.EMPTY;\n        }\n        int len = param.length();\n        StringBuilder sb = new StringBuilder(len);\n        for (int i = 0; i < len; i++) {\n            char c = param.charAt(i);\n            if (Character.isUpperCase(c) && i > 0) {\n                sb.append(UNDERLINE);\n            }\n            sb.append(Character.toLowerCase(c));\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 字符串下划线转驼峰格式\n     *\n     * @param param 需要转换的字符串\n     * @return 转换好的字符串\n     */\n    public static String underlineToCamel(String param) {\n        if (isBlank(param)) {\n            return StringPool.EMPTY;\n        }\n        String temp = param.toLowerCase();\n        int len = temp.length();\n        StringBuilder sb = new StringBuilder(len);\n        for (int i = 0; i < len; i++) {\n            char c = temp.charAt(i);\n            if (c == UNDERLINE) {\n                if (++i < len) {\n                    sb.append(Character.toUpperCase(temp.charAt(i)));\n                }\n            } else {\n                sb.append(c);\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 首字母转换小写\n     *\n     * @param param 需要转换的字符串\n     * @return 转换好的字符串\n     */\n    public static String firstToLowerCase(String param) {\n        if (isBlank(param)) {\n            return StringPool.EMPTY;\n        }\n        return param.substring(0, 1).toLowerCase() + param.substring(1);\n    }\n\n    /**\n     * 正则表达式匹配\n     *\n     * @param regex 正则表达式字符串\n     * @param input 要匹配的字符串\n     * @return 如果 input 符合 regex 正则表达式格式, 返回true, 否则返回 false;\n     */\n    public static boolean matches(String regex, String input) {\n        if (null == regex || null == input) {\n            return false;\n        }\n        return Pattern.matches(regex, input);\n    }\n\n    /**\n     * 替换 SQL 语句中的占位符，例如输入 SELECT * FROM test WHERE id = {0} AND name = {1} 会被替换为\n     * SELECT * FROM test WHERE id = 1 AND name = 'MP'\n     * <p>\n     * 当数组中参数不足时，该方法会抛出错误：数组下标越界{@link ArrayIndexOutOfBoundsException}\n     * </p>\n     *\n     * @param content 填充内容\n     * @param args    填充参数\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static String sqlArgsFill(String content, Object... args) {\n        if (StringUtils.isNotBlank(content) && ArrayUtils.isNotEmpty(args)) {\n            // 索引不能使用，因为 SQL 中的占位符数字与索引不相同\n            BiIntFunction<Matcher, CharSequence> handler = (m, i) -> sqlParam(args[Integer.parseInt(m.group(\"idx\"))]);\n            return replace(content, MP_SQL_PLACE_HOLDER, handler).toString();\n        }\n        return content;\n    }\n\n    /**\n     * 根据指定的表达式替换字符串中指定格式的部分\n     * <p>\n     * BiIntFunction 中的 第二个 参数将传递 参数在字符串中的索引\n     * </p>\n     *\n     * @param src      源字符串\n     * @param ptn      需要替换部分的正则表达式\n     * @param replacer 替换处理器\n     * @return 返回字符串构建起\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static StringBuilder replace(CharSequence src, Pattern ptn, BiIntFunction<Matcher, CharSequence> replacer) {\n        int idx = 0, last = 0, len = src.length();\n        Matcher m = ptn.matcher(src);\n        StringBuilder sb = new StringBuilder();\n\n        // 扫描一次字符串\n        while (m.find()) {\n            sb.append(src, last, m.start()).append(replacer.apply(m, idx++));\n            last = m.end();\n        }\n        // 如果表达式没有匹配或者匹配未到末尾，该判断保证字符串完整性\n        if (last < len) {\n            sb.append(src, last, len);\n        }\n\n        return sb;\n    }\n\n    /**\n     * 获取SQL PARAMS字符串\n     *\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static String sqlParam(Object obj) {\n        String repStr;\n        if (obj instanceof Collection) {\n            repStr = StringUtils.quotaMarkList((Collection<?>) obj);\n        } else {\n            repStr = StringUtils.quotaMark(obj);\n        }\n        return repStr;\n    }\n\n    /**\n     * 使用单引号包含字符串\n     *\n     * @param obj 原字符串\n     * @return 单引号包含的原字符串\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static String quotaMark(Object obj) {\n        String srcStr = String.valueOf(obj);\n        if (obj instanceof CharSequence) {\n            // fix #79\n            return StringEscape.escapeString(srcStr);\n        }\n        return srcStr;\n    }\n\n    /**\n     * 使用单引号包含字符串\n     *\n     * @param coll 集合\n     * @return 单引号包含的原字符串的集合形式\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static String quotaMarkList(Collection<?> coll) {\n        return coll.stream().map(StringUtils::quotaMark)\n            .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));\n    }\n\n    /**\n     * 拼接字符串第二个字符串第一个字母大写\n     */\n    public static String concatCapitalize(String concatStr, final String str) {\n        if (isBlank(concatStr)) {\n            concatStr = StringPool.EMPTY;\n        }\n        if (str == null || str.length() == 0) {\n            return str;\n        }\n\n        final char firstChar = str.charAt(0);\n        if (Character.isTitleCase(firstChar)) {\n            // already capitalized\n            return str;\n        }\n\n        return concatStr + Character.toTitleCase(firstChar) + str.substring(1);\n    }\n\n    /**\n     * 判断对象是否不为空\n     *\n     * @param object ignore\n     * @return ignore\n     */\n    public static boolean checkValNotNull(Object object) {\n        if (object instanceof CharSequence) {\n            return isNotEmpty((CharSequence) object);\n        }\n        return object != null;\n    }\n\n    /**\n     * 判断对象是否为空\n     *\n     * @param object ignore\n     * @return ignore\n     */\n    public static boolean checkValNull(Object object) {\n        return !checkValNotNull(object);\n    }\n\n    /**\n     * 包含大写字母\n     *\n     * @param word 待判断字符串\n     * @return ignore\n     */\n    public static boolean containsUpperCase(String word) {\n        for (int i = 0; i < word.length(); i++) {\n            char c = word.charAt(i);\n            if (Character.isUpperCase(c)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 是否为大写命名\n     *\n     * @param word 待判断字符串\n     * @return ignore\n     */\n    public static boolean isCapitalMode(String word) {\n        return null != word && CAPITAL_MODE.matcher(word).matches();\n    }\n\n    /**\n     * 是否为驼峰下划线混合命名\n     *\n     * @param word 待判断字符串\n     * @return ignore\n     */\n    public static boolean isMixedMode(String word) {\n        return matches(\".*[A-Z]+.*\", word) && matches(\".*[/_]+.*\", word);\n    }\n\n    /**\n     * 判断是否以某个字符串结尾（区分大小写）\n     * Check if a String ends with a specified suffix.\n     * <p>\n     * <code>null</code>s are handled without exceptions. Two <code>null</code>\n     * references are considered to be equal. The comparison is case sensitive.\n     * </p>\n     * <p>\n     * <pre>\n     * StringUtils.endsWith(null, null)      = true\n     * StringUtils.endsWith(null, \"abcdef\")  = false\n     * StringUtils.endsWith(\"def\", null)     = false\n     * StringUtils.endsWith(\"def\", \"abcdef\") = true\n     * StringUtils.endsWith(\"def\", \"ABCDEF\") = false\n     * </pre>\n     * </p>\n     *\n     * @param str    the String to check, may be null\n     * @param suffix the suffix to find, may be null\n     * @return <code>true</code> if the String ends with the suffix, case\n     * sensitive, or both <code>null</code>\n     * @see String#endsWith(String)\n     * @since 2.4\n     */\n    public static boolean endsWith(String str, String suffix) {\n        return endsWith(str, suffix, false);\n    }\n\n    /**\n     * Check if a String ends with a specified suffix (optionally case\n     * insensitive).\n     *\n     * @param str        the String to check, may be null\n     * @param suffix     the suffix to find, may be null\n     * @param ignoreCase inidicates whether the compare should ignore case (case\n     *                   insensitive) or not.\n     * @return <code>true</code> if the String starts with the prefix or both\n     * <code>null</code>\n     * @see String#endsWith(String)\n     */\n    private static boolean endsWith(String str, String suffix, boolean ignoreCase) {\n        if (str == null || suffix == null) {\n            return (str == null && suffix == null);\n        }\n        if (suffix.length() > str.length()) {\n            return false;\n        }\n        int strOffset = str.length() - suffix.length();\n        return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());\n    }\n\n    /**\n     * 是否为CharSequence类型\n     *\n     * @param clazz class\n     * @return true 为是 CharSequence 类型\n     */\n    public static boolean isCharSequence(Class<?> clazz) {\n        return clazz != null && CharSequence.class.isAssignableFrom(clazz);\n    }\n\n    /**\n     * 前n个首字母小写,之后字符大小写的不变\n     *\n     * @param rawString 需要处理的字符串\n     * @param index     多少个字符(从左至右)\n     * @return ignore\n     */\n    public static String prefixToLower(String rawString, int index) {\n        StringBuilder field = new StringBuilder();\n        field.append(rawString.substring(0, index).toLowerCase());\n        field.append(rawString.substring(index));\n        return field.toString();\n    }\n\n    /**\n     * 删除字符前缀之后,首字母小写,之后字符大小写的不变\n     * <p>StringUtils.removePrefixAfterPrefixToLower( \"isUser\", 2 )     = user</p>\n     * <p>StringUtils.removePrefixAfterPrefixToLower( \"isUserInfo\", 2 ) = userInfo</p>\n     *\n     * @param rawString 需要处理的字符串\n     * @param index     删除多少个字符(从左至右)\n     * @return ignore\n     */\n    public static String removePrefixAfterPrefixToLower(String rawString, int index) {\n        return prefixToLower(rawString.substring(index), 1);\n    }\n\n    /**\n     * 驼峰转连字符\n     * <p>StringUtils.camelToHyphen( \"managerAdminUserService\" ) = manager-admin-user-service</p>\n     *\n     * @param input ignore\n     * @return 以'-'分隔\n     * @see <a href=\"https://github.com/krasa/StringManipulation\">document</a>\n     */\n    public static String camelToHyphen(String input) {\n        return wordsToHyphenCase(wordsAndHyphenAndCamelToConstantCase(input));\n    }\n\n    private static String wordsAndHyphenAndCamelToConstantCase(String input) {\n        StringBuilder buf = new StringBuilder();\n        char previousChar = ' ';\n        char[] chars = input.toCharArray();\n        for (char c : chars) {\n            boolean isUpperCaseAndPreviousIsLowerCase = (Character.isLowerCase(previousChar)) && (Character.isUpperCase(c));\n\n            boolean previousIsWhitespace = Character.isWhitespace(previousChar);\n            boolean lastOneIsNotUnderscore = (buf.length() > 0) && (buf.charAt(buf.length() - 1) != '_');\n            boolean isNotUnderscore = c != '_';\n            if (lastOneIsNotUnderscore && (isUpperCaseAndPreviousIsLowerCase || previousIsWhitespace)) {\n                buf.append(StringPool.UNDERSCORE);\n            } else if ((Character.isDigit(previousChar) && Character.isLetter(c))) {\n                buf.append(UNDERLINE);\n            }\n            if ((shouldReplace(c)) && (lastOneIsNotUnderscore)) {\n                buf.append(UNDERLINE);\n            } else if (!Character.isWhitespace(c) && (isNotUnderscore || lastOneIsNotUnderscore)) {\n                buf.append(Character.toUpperCase(c));\n            }\n            previousChar = c;\n        }\n        if (Character.isWhitespace(previousChar)) {\n            buf.append(StringPool.UNDERSCORE);\n        }\n        return buf.toString();\n    }\n\n    private static boolean shouldReplace(char c) {\n        return (c == '.') || (c == '_') || (c == '-');\n    }\n\n    private static String wordsToHyphenCase(String s) {\n        StringBuilder buf = new StringBuilder();\n        char lastChar = 'a';\n        for (char c : s.toCharArray()) {\n            if ((Character.isWhitespace(lastChar)) && (!Character.isWhitespace(c))\n                && ('-' != c) && (buf.length() > 0)\n                && (buf.charAt(buf.length() - 1) != '-')) {\n                buf.append(StringPool.DASH);\n            }\n            if ('_' == c) {\n                buf.append(StringPool.DASH);\n            } else if ('.' == c) {\n                buf.append(StringPool.DASH);\n            } else if (!Character.isWhitespace(c)) {\n                buf.append(Character.toLowerCase(c));\n            }\n            lastChar = c;\n        }\n        if (Character.isWhitespace(lastChar)) {\n            buf.append(StringPool.DASH);\n        }\n        return buf.toString();\n    }\n\n    /**\n     * <p>比较两个字符串，相同则返回true。字符串可为null</p>\n     *\n     * <p>对字符串大小写敏感</p>\n     *\n     * <pre>\n     * StringUtils.equals(null, null)   = true\n     * StringUtils.equals(null, \"abc\")  = false\n     * StringUtils.equals(\"abc\", null)  = false\n     * StringUtils.equals(\"abc\", \"abc\") = true\n     * StringUtils.equals(\"abc\", \"ABC\") = false\n     * </pre>\n     *\n     * @param cs1 第一个字符串, 可为 {@code null}\n     * @param cs2 第二个字符串, 可为 {@code null}\n     * @return {@code true} 如果两个字符串相同, 或者都为 {@code null}\n     * @see Object#equals(Object)\n     */\n    public static boolean equals(final CharSequence cs1, final CharSequence cs2) {\n        if (cs1 == cs2) {\n            return true;\n        }\n        if (cs1 == null || cs2 == null) {\n            return false;\n        }\n        if (cs1.length() != cs2.length()) {\n            return false;\n        }\n        if (cs1 instanceof String && cs2 instanceof String) {\n            return cs1.equals(cs2);\n        }\n        // Step-wise comparison\n        final int length = cs1.length();\n        for (int i = 0; i < length; i++) {\n            if (cs1.charAt(i) != cs2.charAt(i)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * SQL 注入字符串去除空白内容：\n     * <ul>\n     *     <li>\\n 回车</li>\n     *     <li>\\t 水平制表符</li>\n     *     <li>\\s 空格</li>\n     *     <li>\\r 换行</li>\n     * </ul>\n     *\n     * @param str 字符串\n     */\n    public static String sqlInjectionReplaceBlank(String str) {\n        if (SqlInjectionUtils.check(str)) {\n            /**\n             * 过滤sql黑名单字符，存在 SQL 注入，去除空白内容\n             */\n            str = replaceAllBlank(str);\n\n        }\n        return str;\n    }\n\n    /**\n     * 字符串去除空白内容：\n     * <ul>\n     *     <li>\\n 回车</li>\n     *     <li>\\t 水平制表符</li>\n     *     <li>\\s 空格</li>\n     *     <li>\\r 换行</li>\n     * </ul>\n     *\n     * @param str 字符串\n     */\n    public static String replaceAllBlank(String str) {\n        Matcher matcher = REPLACE_BLANK.matcher(str);\n        return matcher.replaceAll(\"\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/SystemClock.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.sql.Timestamp;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * 高并发场景下System.currentTimeMillis()的性能问题的优化\n *\n * <p>System.currentTimeMillis()的调用比new一个普通对象要耗时的多（具体耗时高出多少我还没测试过，有人说是100倍左右）</p>\n * <p>System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道</p>\n * <p>后台定时更新时钟，JVM退出时，线程自动回收</p>\n * <p>10亿：43410,206,210.72815533980582%</p>\n * <p>1亿：4699,29,162.0344827586207%</p>\n * <p>1000万：480,12,40.0%</p>\n * <p>100万：50,10,5.0%</p>\n *\n * @author hubin\n * @since 2016-08-01\n */\npublic class SystemClock {\n\n    private final long period;\n    private final AtomicLong now;\n\n    private SystemClock(long period) {\n        this.period = period;\n        this.now = new AtomicLong(System.currentTimeMillis());\n        scheduleClockUpdating();\n    }\n\n    private static SystemClock instance() {\n        return InstanceHolder.INSTANCE;\n    }\n\n    public static long now() {\n        return instance().currentTimeMillis();\n    }\n\n    public static String nowDate() {\n        return new Timestamp(instance().currentTimeMillis()).toString();\n    }\n\n    private void scheduleClockUpdating() {\n        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {\n            Thread thread = new Thread(runnable, \"System Clock\");\n            thread.setDaemon(true);\n            return thread;\n        });\n        scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);\n    }\n\n    private long currentTimeMillis() {\n        return now.get();\n    }\n\n    private static class InstanceHolder {\n        public static final SystemClock INSTANCE = new SystemClock(1);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/TableNameParser.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * SQL 表名解析\n * <p>\n * <a href=\"https://github.com/mnadeem/sql-table-name-parser\">sql-table-name-parser</a>\n * <p>\n * Ultra light, Ultra fast parser to extract table name out SQLs, supports oracle dialect SQLs as well.\n * USE: new TableNameParser(sql).tables()\n *\n * @author Nadeem Mohammad, hcl\n * @since 2019-04-22\n */\npublic final class TableNameParser {\n\n    private static final String TOKEN_SET = \"set\";\n    private static final String TOKEN_OF = \"of\";\n    private static final String TOKEN_DUAL = \"dual\";\n    private static final String IGNORE = \"ignore\";\n    private static final String TOKEN_DELETE = \"delete\";\n    private static final String TOKEN_UPDATE = \"update\";\n    private static final String TOKEN_CREATE = \"create\";\n    private static final String TOKEN_INDEX = \"index\";\n\n    private static final String KEYWORD_JOIN = \"join\";\n    private static final String KEYWORD_INTO = \"into\";\n    private static final String KEYWORD_TABLE = \"table\";\n    private static final String KEYWORD_FROM = \"from\";\n    private static final String KEYWORD_USING = \"using\";\n    private static final String KEYWORD_UPDATE = \"update\";\n    private static final String KEYWORD_STRAIGHT_JOIN = \"straight_join\";\n    private static final String KEYWORD_DUPLICATE = \"duplicate\";\n\n    private static final List<String> concerned = Arrays.asList(KEYWORD_TABLE, KEYWORD_INTO, KEYWORD_JOIN, KEYWORD_USING, KEYWORD_UPDATE, KEYWORD_STRAIGHT_JOIN);\n    private static final List<String> ignored = Arrays.asList(StringPool.LEFT_BRACKET, TOKEN_SET, TOKEN_OF, TOKEN_DUAL);\n\n    /**\n     * 索引类型\n     *\n     * @since 3.5.11\n     */\n    private static final Set<String> INDEX_TYPES = new HashSet<>(Arrays.asList(\"UNIQUE\", \"FULLTEXT\", \"SPATIAL\", \"CLUSTERED\", \"NONCLUSTERED\"));\n\n    /**\n     * 该表达式会匹配 SQL 中不是 SQL TOKEN 的部分，比如换行符，注释信息，结尾的 {@code ;} 等。\n     * <p>\n     * 排除的项目包括：\n     * 1、以 -- 开头的注释信息\n     * 2、;\n     * 3、空白字符\n     * 4、使用 /* * / 注释的信息\n     * 5、把 ,() 也要分出来\n     */\n    private static final Pattern NON_SQL_TOKEN_PATTERN = Pattern.compile(\"(--[^\\\\v]+)|;|(\\\\s+)|((?s)/[*].*?[*]/)\"\n        + \"|(((\\\\b|\\\\B)(?=[,()]))|((?<=[,()])(\\\\b|\\\\B)))\"\n    );\n\n    private final List<SqlToken> tokens;\n\n    /**\n     * 从 SQL 中提取表名称\n     *\n     * @param sql 需要解析的 SQL 语句\n     */\n    public TableNameParser(String sql) {\n        tokens = fetchAllTokens(sql);\n    }\n\n    /**\n     * 接受一个新的访问者，并访问当前 SQL 的表名称\n     * <p>\n     * 现在我们改成了访问者模式，不在对以前的 SQL 做改动\n     * 同时，你可以方便的获得表名位置的索引\n     *\n     * @param visitor 访问者\n     */\n    public void accept(TableNameVisitor visitor) {\n        int index = 0;\n        String first = tokens.get(index).getValue();\n        if (isOracleSpecialDelete(first, tokens, index)) {\n            visitNameToken(safeGetToken(index + 1), visitor);\n        } else if (isCreateIndex(first, tokens, index)) {\n            String value = tokens.get(index + 4).getValue();\n            if(\"ON\".equalsIgnoreCase(value)) {\n                visitNameToken(safeGetToken(index + 5), visitor);\n            } else {\n                visitNameToken(safeGetToken(index + 4), visitor);\n            }\n        } else if (isCreateTableIfNotExist(first, tokens, index)) {\n            visitNameToken(safeGetToken(index + 5), visitor);\n        } else {\n            while (hasMoreTokens(tokens, index)) {\n                String current = tokens.get(index++).getValue();\n                if (isFromToken(current)) {\n                    processFromToken(tokens, index, visitor);\n                } else if (isOnDuplicateKeyUpdate(current, index)) {\n                    index = skipDuplicateKeyUpdateIndex(index);\n                } else if (concerned.contains(current.toLowerCase())) {\n                    if (hasMoreTokens(tokens, index)) {\n                        SqlToken next = tokens.get(index++);\n                        if (TOKEN_UPDATE.equalsIgnoreCase(current)\n                            && IGNORE.equalsIgnoreCase(next.getValue())) {\n                            next = tokens.get(index++);\n                        }\n                        visitNameToken(next, visitor);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * 安全访问获取SqlToken\n     *\n     * @param index 索引\n     * @return 超出索引返回 null，否则返回SqlToken\n     * @since 3.5.11\n     */\n    private SqlToken safeGetToken(int index) {\n        return index < tokens.size() ? tokens.get(index) : null;\n    }\n\n    /**\n     * 表名访问器\n     */\n    public interface TableNameVisitor {\n        /**\n         * @param name 表示表名称的 token\n         */\n        void visit(SqlToken name);\n    }\n\n    /**\n     * 从 SQL 语句中提取出 所有的 SQL Token\n     *\n     * @param sql SQL\n     * @return 语句\n     */\n    private List<SqlToken> fetchAllTokens(String sql) {\n        List<SqlToken> tokens = new ArrayList<>();\n        Matcher matcher = NON_SQL_TOKEN_PATTERN.matcher(sql);\n        int last = 0;\n        while (matcher.find()) {\n            int start = matcher.start();\n            if (start != last) {\n                tokens.add(new SqlToken(last, start, sql.substring(last, start)));\n            }\n            last = matcher.end();\n        }\n        if (last != sql.length()) {\n            tokens.add(new SqlToken(last, sql.length(), sql.substring(last)));\n        }\n        return tokens;\n    }\n\n    /**\n     * 如果是 DELETE 后面紧跟的不是 FROM 或者 * ,则 返回 true\n     *\n     * @param current 当前的 token\n     * @param tokens  token 列表\n     * @param index   索引\n     * @return 判断是不是 Oracle 特殊的删除手法\n     */\n    private static boolean isOracleSpecialDelete(String current, List<SqlToken> tokens, int index) {\n        if (TOKEN_DELETE.equalsIgnoreCase(current)) {\n            if (hasMoreTokens(tokens, index++)) {\n                String next = tokens.get(index).getValue();\n                return !KEYWORD_FROM.equalsIgnoreCase(next) && !StringPool.ASTERISK.equals(next);\n            }\n        }\n        return false;\n    }\n\n    // CREATE INDEX temp_name_idx ON table1(name) NOLOGGING PARALLEL (DEGREE 8);\n    // CREATE FULLTEXT INDEX ft_users_content ON users(content);\n    private boolean isCreateIndex(String current, List<SqlToken> tokens, int index) {\n        if (TOKEN_CREATE.equalsIgnoreCase(current) && hasMoreTokens(tokens, index + 4)) {\n            String next = tokens.get(index + 1).getValue();\n            if (INDEX_TYPES.contains(next.toUpperCase())) {\n                next = tokens.get(index + 2).getValue();\n            }\n            return TOKEN_INDEX.equalsIgnoreCase(next);\n        }\n        return false;\n    }\n\n    //create table if not exists `user_info`\n    private boolean isCreateTableIfNotExist(String current, List<SqlToken> tokens, int index) {\n        if (TOKEN_CREATE.equalsIgnoreCase(current) && hasMoreTokens(tokens, index + 5)) {\n            StringBuilder tableIfNotExist = new StringBuilder();\n            for (int i = index; i <= index + 4; i++) {\n                tableIfNotExist.append(tokens.get(i).getValue());\n            }\n            return \"createtableifnotexists\".equalsIgnoreCase(tableIfNotExist.toString());\n        }\n        return false;\n    }\n\n    /**\n     * @param current 当前token\n     * @param index   索引\n     * @return 判断是否是mysql的特殊语法 on duplicate key update\n     */\n    private boolean isOnDuplicateKeyUpdate(String current, int index) {\n        if (KEYWORD_DUPLICATE.equalsIgnoreCase(current)) {\n            if (hasMoreTokens(tokens, index++)) {\n                String next = tokens.get(index).getValue();\n                return KEYWORD_UPDATE.equalsIgnoreCase(next);\n            }\n        }\n        return false;\n    }\n\n    /**\n     * @deprecated 3.5.11 建议使用 {@link #hasMoreTokens(List, int)} 判断\n     */\n    private static boolean hasIthToken(List<SqlToken> tokens, int currentIndex) {\n        return hasMoreTokens(tokens, currentIndex) && tokens.size() > currentIndex + 3;\n    }\n\n    private static boolean isFromToken(String currentToken) {\n        return KEYWORD_FROM.equalsIgnoreCase(currentToken);\n    }\n\n    private int skipDuplicateKeyUpdateIndex(int index) {\n        // on duplicate key update为mysql的固定写法，直接跳过即可。\n        return index + 2;\n    }\n\n    private static void processFromToken(List<SqlToken> tokens, int index, TableNameVisitor visitor) {\n        SqlToken sqlToken = tokens.get(index++);\n        visitNameToken(sqlToken, visitor);\n\n        String next = null;\n        if (hasMoreTokens(tokens, index)) {\n            next = tokens.get(index++).getValue();\n        }\n\n        if (shouldProcessMultipleTables(next)) {\n            processNonAliasedMultiTables(tokens, index, next, visitor);\n        } else {\n            processAliasedMultiTables(tokens, index, sqlToken, visitor);\n        }\n    }\n\n    private static void processNonAliasedMultiTables(List<SqlToken> tokens, int index, String nextToken, TableNameVisitor visitor) {\n        while (nextToken.equals(StringPool.COMMA)) {\n            visitNameToken(tokens.get(index++), visitor);\n            if (hasMoreTokens(tokens, index)) {\n                nextToken = tokens.get(index++).getValue();\n            } else {\n                break;\n            }\n        }\n    }\n\n    private static void processAliasedMultiTables(List<SqlToken> tokens, int index, SqlToken current, TableNameVisitor visitor) {\n        String nextNextToken = null;\n        if (hasMoreTokens(tokens, index)) {\n            nextNextToken = tokens.get(index++).getValue();\n        }\n\n        if (shouldProcessMultipleTables(nextNextToken)) {\n            while (hasMoreTokens(tokens, index) && nextNextToken.equals(StringPool.COMMA)) {\n                if (hasMoreTokens(tokens, index)) {\n                    current = tokens.get(index++);\n                }\n                if (hasMoreTokens(tokens, index)) {\n                    index++;\n                }\n                if (hasMoreTokens(tokens, index)) {\n                    nextNextToken = tokens.get(index++).getValue();\n                }\n                visitNameToken(current, visitor);\n            }\n        }\n    }\n\n    private static boolean shouldProcessMultipleTables(final String nextToken) {\n        return nextToken != null && nextToken.equals(StringPool.COMMA);\n    }\n\n    private static boolean hasMoreTokens(List<SqlToken> tokens, int index) {\n        return index < tokens.size();\n    }\n\n    private static void visitNameToken(SqlToken token, TableNameVisitor visitor) {\n        if (token != null) {\n            String value = token.getValue().toLowerCase();\n            if (!ignored.contains(value)) {\n                visitor.visit(token);\n            }\n        }\n    }\n\n    /**\n     * parser tables\n     *\n     * @return table names extracted out of sql\n     * @see #accept(TableNameVisitor)\n     */\n    public Collection<String> tables() {\n        Map<String, String> tableMap = new HashMap<>();\n        accept(token -> {\n            String name = token.getValue();\n            tableMap.putIfAbsent(name.toLowerCase(), name);\n        });\n        return new HashSet<>(tableMap.values());\n    }\n\n    /**\n     * SQL 词\n     */\n    public static class SqlToken implements Comparable<SqlToken> {\n        private final int start;\n        private final int end;\n        private final String value;\n\n        private SqlToken(int start, int end, String value) {\n            this.start = start;\n            this.end = end;\n            this.value = value;\n        }\n\n        public int getStart() {\n            return start;\n        }\n\n        public int getEnd() {\n            return end;\n        }\n\n        public String getValue() {\n            return value;\n        }\n\n        @Override\n        public int compareTo(SqlToken o) {\n            return Integer.compare(start, o.start);\n        }\n\n        @Override\n        public String toString() {\n            return value;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/Wrappers.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * Wrapper 条件构造\n *\n * @author Caratacus\n */\npublic final class Wrappers {\n\n    /**\n     * 空的 EmptyWrapper\n     */\n    private static final QueryWrapper<?> emptyWrapper = new EmptyWrapper<>();\n\n    private Wrappers() {\n        // ignore\n    }\n\n    /**\n     * 获取 QueryWrapper&lt;T&gt;\n     *\n     * @param <T> 实体类泛型\n     * @return QueryWrapper&lt;T&gt;\n     */\n    public static <T> QueryWrapper<T> query() {\n        return new QueryWrapper<>();\n    }\n\n    /**\n     * 获取 QueryWrapper&lt;T&gt;\n     *\n     * @param entity 实体类\n     * @param <T>    实体类泛型\n     * @return QueryWrapper&lt;T&gt;\n     */\n    public static <T> QueryWrapper<T> query(T entity) {\n        return new QueryWrapper<>(entity);\n    }\n\n    /**\n     * 获取 QueryWrapper&lt;T&gt;\n     *\n     * @param entityClass 实体类class\n     * @param <T>    实体类泛型\n     * @return QueryWrapper&lt;T&gt;\n     */\n    public static <T> QueryWrapper<T> query(Class<T> entityClass) {\n        return new QueryWrapper<>(entityClass);\n    }\n\n    /**\n     * 获取 LambdaQueryWrapper&lt;T&gt;\n     *\n     * @param <T> 实体类泛型\n     * @return LambdaQueryWrapper&lt;T&gt;\n     */\n    public static <T> LambdaQueryWrapper<T> lambdaQuery() {\n        return new LambdaQueryWrapper<>();\n    }\n\n    /**\n     * 获取 LambdaQueryWrapper&lt;T&gt;\n     *\n     * @param entity 实体类\n     * @param <T>    实体类泛型\n     * @return LambdaQueryWrapper&lt;T&gt;\n     */\n    public static <T> LambdaQueryWrapper<T> lambdaQuery(T entity) {\n        return new LambdaQueryWrapper<>(entity);\n    }\n\n    /**\n     * 获取 LambdaQueryWrapper&lt;T&gt;\n     *\n     * @param entityClass 实体类class\n     * @param <T>         实体类泛型\n     * @return LambdaQueryWrapper&lt;T&gt;\n     * @since 3.3.1\n     */\n    public static <T> LambdaQueryWrapper<T> lambdaQuery(Class<T> entityClass) {\n        return new LambdaQueryWrapper<>(entityClass);\n    }\n\n    /**\n     * 获取 UpdateWrapper&lt;T&gt;\n     *\n     * @param <T> 实体类泛型\n     * @return UpdateWrapper&lt;T&gt;\n     */\n    public static <T> UpdateWrapper<T> update() {\n        return new UpdateWrapper<>();\n    }\n\n    /**\n     * 获取 UpdateWrapper&lt;T&gt;\n     *\n     * @param entity 实体类\n     * @param <T>    实体类泛型\n     * @return UpdateWrapper&lt;T&gt;\n     */\n    public static <T> UpdateWrapper<T> update(T entity) {\n        return new UpdateWrapper<>(entity);\n    }\n\n    /**\n     * 获取 LambdaUpdateWrapper&lt;T&gt;\n     *\n     * @param <T> 实体类泛型\n     * @return LambdaUpdateWrapper&lt;T&gt;\n     */\n    public static <T> LambdaUpdateWrapper<T> lambdaUpdate() {\n        return new LambdaUpdateWrapper<>();\n    }\n\n    /**\n     * 获取 LambdaUpdateWrapper&lt;T&gt;\n     *\n     * @param entity 实体类\n     * @param <T>    实体类泛型\n     * @return LambdaUpdateWrapper&lt;T&gt;\n     */\n    public static <T> LambdaUpdateWrapper<T> lambdaUpdate(T entity) {\n        return new LambdaUpdateWrapper<>(entity);\n    }\n\n    /**\n     * 获取 LambdaUpdateWrapper&lt;T&gt;\n     *\n     * @param entityClass 实体类class\n     * @param <T>         实体类泛型\n     * @return LambdaUpdateWrapper&lt;T&gt;\n     * @since 3.3.1\n     */\n    public static <T> LambdaUpdateWrapper<T> lambdaUpdate(Class<T> entityClass) {\n        return new LambdaUpdateWrapper<>(entityClass);\n    }\n\n    /**\n     * 获取 EmptyWrapper&lt;T&gt;\n     *\n     * @param <T> 任意泛型\n     * @return EmptyWrapper&lt;T&gt;\n     * @see EmptyWrapper\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> QueryWrapper<T> emptyWrapper() {\n        return (QueryWrapper<T>) emptyWrapper;\n    }\n\n    /**\n     * 一个空的QueryWrapper子类该类不包含任何条件\n     *\n     * @param <T>\n     * @see com.baomidou.mybatisplus.core.conditions.query.QueryWrapper\n     */\n    private static class EmptyWrapper<T> extends QueryWrapper<T> {\n\n        private static final long serialVersionUID = -2515957613998092272L;\n\n        @Override\n        public T getEntity() {\n            return null;\n        }\n\n        @Override\n        public EmptyWrapper<T> setEntity(T entity) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public QueryWrapper<T> setEntityClass(Class<T> entityClass) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public Class<T> getEntityClass() {\n            return null;\n        }\n\n        @Override\n        public String getSqlSelect() {\n            return null;\n        }\n\n        @Override\n        public MergeSegments getExpression() {\n            return null;\n        }\n\n        @Override\n        public boolean isEmptyOfWhere() {\n            return true;\n        }\n\n        @Override\n        public boolean isEmptyOfNormal() {\n            return true;\n        }\n\n        @Override\n        public boolean isNonEmptyOfEntity() {\n            return !isEmptyOfEntity();\n        }\n\n        @Override\n        public boolean isEmptyOfEntity() {\n            return true;\n        }\n\n        @Override\n        protected void initNeed() {\n        }\n\n        @Override\n        public EmptyWrapper<T> last(boolean condition, String lastSql) {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public String getSqlSegment() {\n            return null;\n        }\n\n        @Override\n        public Map<String, Object> getParamNameValuePairs() {\n            return Collections.emptyMap();\n        }\n\n        @Override\n        protected String columnsToString(String... columns) {\n            return null;\n        }\n\n        @Override\n        protected String columnToString(String column) {\n            return null;\n        }\n\n        @Override\n        protected EmptyWrapper<T> instance() {\n            throw new UnsupportedOperationException();\n        }\n\n        @Override\n        public void clear() {\n            throw new UnsupportedOperationException();\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 工具类\n *\n * @see com.baomidou.mybatisplus.core.toolkit.LambdaUtils Lambda工具类\n */\npackage com.baomidou.mybatisplus.core.toolkit;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/reflect/GenericTypeUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.reflect;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\n/**\n * 泛型类工具（用于隔离Spring的代码）\n *\n * @author noear\n * @author hubin\n * @since 2021-09-03\n */\npublic class GenericTypeUtils {\n\n    /**\n     * 能否加载SpringCore包\n     *\n     * @since 3.5.4\n     */\n    private static boolean loadSpringCore = false;\n\n    static {\n        try {\n            ClassUtils.toClassConfident(\"org.springframework.core.GenericTypeResolver\");\n            loadSpringCore = true;\n        } catch (Exception exception) {\n            // ignore\n        }\n    }\n    private static IGenericTypeResolver GENERIC_TYPE_RESOLVER;\n\n    /**\n     * 获取泛型工具助手\n     */\n    public static Class<?>[] resolveTypeArguments(final Class<?> clazz, final Class<?> genericIfc) {\n        if (null == GENERIC_TYPE_RESOLVER) {\n            // 直接使用 spring 静态方法，减少对象创建\n            return SpringReflectionHelper.resolveTypeArguments(clazz, genericIfc);\n        }\n        return GENERIC_TYPE_RESOLVER.resolveTypeArguments(clazz, genericIfc);\n    }\n\n    /**\n     * 设置泛型工具助手。如果不想使用Spring封装，可以使用前替换掉\n     */\n    public static void setGenericTypeResolver(IGenericTypeResolver genericTypeResolver) {\n        GENERIC_TYPE_RESOLVER = genericTypeResolver;\n    }\n\n    /**\n     * 判断是否有自定泛型提取类或能否加载SpringCore进行泛型提取\n     *\n     * @return 是否有实现\n     * @since 3.5.4\n     */\n    public static boolean hasGenericTypeResolver() {\n        return GENERIC_TYPE_RESOLVER != null || loadSpringCore;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/reflect/IGenericTypeResolver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.reflect;\n\n/**\n * 泛型类助手（用于隔离Spring的代码）\n *\n * @author noear\n * @author hubin\n * @since 2021-09-03\n */\npublic interface IGenericTypeResolver {\n\n    Class<?>[] resolveTypeArguments(final Class<?> clazz, final Class<?> genericIfc);\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/reflect/SpringReflectionHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.reflect;\n\nimport org.springframework.core.GenericTypeResolver;\n\n/**\n * Spring 反射辅助类\n *\n * @author noear\n * @author hubin\n * @since 2021-09-03\n */\npublic class SpringReflectionHelper {\n\n    public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {\n        return GenericTypeResolver.resolveTypeArguments(clazz, genericIfc);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/reflect/TypeParameterResolver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.reflect;\n\nimport java.lang.reflect.GenericDeclaration;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * 类型参数实现收集器，采集类型实现中各个类型参数的实际值\n * <p>\n * Create by hcl at 2023/9/25\n */\npublic class TypeParameterResolver {\n    private final Map<TypeVariable<?>, Type> map;\n    private final Set<Type> distinct;\n\n    protected TypeParameterResolver(Map<TypeVariable<?>, Type> map) {\n        this.map = map;\n        this.distinct = new HashSet<>();\n    }\n\n    /**\n     * 获取类型上指定索引位置参数的实现信息\n     *\n     * @param source 类型\n     * @param index  索引\n     * @param type   实现类型\n     * @return 返回类型实现或者 null\n     */\n    public static Type resolveClassIndexedParameter(Type type, Class<?> source, int index) {\n        return calculateParameterValue(resolveParameterValues(type), source.getTypeParameters()[index]);\n    }\n\n    /**\n     * 计算参数值\n     *\n     * @param map       变量 Map\n     * @param parameter 参数\n     * @return 返回参数值\n     */\n    public static Type calculateParameterValue(Map<TypeVariable<?>, Type> map, TypeVariable<?> parameter) {\n        Type res = map.get(parameter);\n        while (res instanceof TypeVariable<?>) {\n            res = map.get(res);\n        }\n        return res;\n    }\n\n    /**\n     * 解析指定类型下的泛型参数实现信息\n     *\n     * @param from 起始类型\n     * @return 返回全部的泛型参数及其映射类型值\n     */\n    public static Map<TypeVariable<?>, Type> resolveParameterValues(Type from) {\n        Map<TypeVariable<?>, Type> map = new HashMap<>();\n        new TypeParameterResolver(map).visitType(from);\n        return map;\n    }\n\n    /**\n     * 访问类型，类型中需要关注两个：{@link Class} 和 {@link ParameterizedType}\n     *\n     * @param type 类型\n     */\n    public void visitType(Type type) {\n        if (!distinct.add(type)) {\n            return;\n        }\n\n        if (type instanceof Class<?>) {\n            visitClass((Class<?>) type);\n            return;\n        }\n\n        if (type instanceof ParameterizedType) {\n            visitParameterizedType((ParameterizedType) type);\n        }\n\n    }\n\n    /**\n     * 访问类型，类型的树可以分解为父类和接口，这两个地方都要解析。\n     *\n     * @param c 类\n     */\n    private void visitClass(Class<?> c) {\n        visitType(c.getGenericSuperclass());\n        for (Type i : c.getGenericInterfaces()) {\n            visitType(i);\n        }\n    }\n\n    /**\n     * 访问参数化类型，类型参数映射的主要逻辑就在这里\n     *\n     * @param parameterized 参数化类型\n     */\n    private void visitParameterizedType(ParameterizedType parameterized) {\n        Type raw = parameterized.getRawType();\n        visitType(raw);\n\n        if (raw instanceof GenericDeclaration) {\n            GenericDeclaration declaration = (GenericDeclaration) raw;\n            TypeVariable<?>[] parameters = declaration.getTypeParameters();\n            Type[] arguments = parameterized.getActualTypeArguments();\n            for (int i = 0; i < parameters.length; i++) {\n                TypeVariable<?> parameter = parameters[i];\n                Type argument = arguments[i];\n                map.put(parameter, argument);\n                visitType(argument);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlInjectionUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport java.util.Objects;\nimport java.util.regex.Pattern;\n\n/**\n * SQL 注入验证工具类\n *\n * @author hubin\n * @since 2021-08-15\n */\npublic class SqlInjectionUtils {\n    /**\n     * SQL语法检查正则：符合两个关键字（有先后顺序）才算匹配\n     */\n    private static final Pattern SQL_SYNTAX_PATTERN = Pattern.compile(\"(insert|delete|update|select|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)\" +\n        \"\\\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)|(select\\\\s*\\\\*\\\\s*from\\\\s+)\" +\n        \"|if\\\\s*\\\\(.*\\\\)|select\\\\s*\\\\(.*\\\\)|substr\\\\s*\\\\(.*\\\\)|substring\\\\s*\\\\(.*\\\\)|char\\\\s*\\\\(.*\\\\)|concat\\\\s*\\\\(.*\\\\)|benchmark\\\\s*\\\\(.*\\\\)|sleep\\\\s*\\\\(.*\\\\)|(and|or)\\\\s+.*\", Pattern.CASE_INSENSITIVE);\n    /**\n     * 使用'、;或注释截断SQL检查正则\n     */\n    private static final Pattern SQL_COMMENT_PATTERN = Pattern.compile(\"'.*(or|union|--|#|/\\\\*|;)\", Pattern.CASE_INSENSITIVE);\n\n\n    /**\n     * 检查参数是否存在 SQL 注入\n     *\n     * @param value 检查参数\n     * @return true 非法 false 合法\n     */\n    public static boolean check(String value) {\n        Objects.requireNonNull(value);\n        // 处理是否包含SQL注释字符 || 检查是否包含SQL注入敏感字符\n        return SQL_COMMENT_PATTERN.matcher(value).find() || SQL_SYNTAX_PATTERN.matcher(value).find();\n    }\n\n    /**\n     * 刪除字段转义符单引号双引号\n     *\n     * @param text 待处理字段\n     */\n    public static String removeEscapeCharacter(String text) {\n        Objects.requireNonNull(text);\n        return text.replaceAll(\"\\\"\", \"\").replaceAll(\"'\", \"\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlScriptUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\n\n/**\n * <p>\n * sql 脚本工具类\n * </p>\n *\n * @author miemie\n * @since 2018-08-15\n */\npublic abstract class SqlScriptUtils implements Constants {\n\n    /**\n     * <p>\n     * 获取 带 if 标签的脚本\n     * </p>\n     *\n     * @param sqlScript sql 脚本片段\n     * @return if 脚本\n     */\n    public static String convertIf(final String sqlScript, final String ifTest, boolean newLine) {\n        String newSqlScript = sqlScript;\n        if (newLine) {\n            newSqlScript = NEWLINE + newSqlScript + NEWLINE;\n        }\n        return String.format(\"<if test=\\\"%s\\\">%s</if>\", ifTest, newSqlScript);\n    }\n\n    /**\n     * <p>\n     * 获取 带 trim 标签的脚本\n     * </p>\n     *\n     * @param sqlScript       sql 脚本片段\n     * @param prefix          以...开头\n     * @param suffix          以...结尾\n     * @param prefixOverrides 干掉最前一个...\n     * @param suffixOverrides 干掉最后一个...\n     * @return trim 脚本\n     */\n    public static String convertTrim(final String sqlScript, final String prefix, final String suffix,\n                                     final String prefixOverrides, final String suffixOverrides) {\n        StringBuilder sb = new StringBuilder(\"<trim\");\n        if (StringUtils.isNotBlank(prefix)) {\n            sb.append(\" prefix=\\\"\").append(prefix).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(suffix)) {\n            sb.append(\" suffix=\\\"\").append(suffix).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(prefixOverrides)) {\n            sb.append(\" prefixOverrides=\\\"\").append(prefixOverrides).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(suffixOverrides)) {\n            sb.append(\" suffixOverrides=\\\"\").append(suffixOverrides).append(QUOTE);\n        }\n        return sb.append(RIGHT_CHEV).append(NEWLINE).append(sqlScript).append(NEWLINE).append(\"</trim>\").toString();\n    }\n\n    /**\n     * <p>\n     * 生成 choose 标签的脚本\n     * </p>\n     *\n     * @param whenTest  when 内 test 的内容\n     * @param otherwise otherwise 内容\n     * @return choose 脚本\n     */\n    public static String convertChoose(final String whenTest, final String whenSqlScript, final String otherwise) {\n        return \"<choose>\" + NEWLINE\n            + \"<when test=\\\"\" + whenTest + QUOTE + RIGHT_CHEV + NEWLINE\n            + whenSqlScript + NEWLINE + \"</when>\" + NEWLINE\n            + \"<otherwise>\" + otherwise + \"</otherwise>\" + NEWLINE\n            + \"</choose>\";\n    }\n\n    /**\n     * <p>\n     * 生成 foreach 标签的脚本\n     * </p>\n     *\n     * @param sqlScript  foreach 内部的 sql 脚本\n     * @param collection collection\n     * @param index      index\n     * @param item       item\n     * @param separator  separator\n     * @return foreach 脚本\n     */\n    public static String convertForeach(final String sqlScript, final String collection, final String index,\n                                        final String item, final String separator) {\n        StringBuilder sb = new StringBuilder(\"<foreach\");\n        if (StringUtils.isNotBlank(collection)) {\n            sb.append(\" collection=\\\"\").append(collection).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(index)) {\n            sb.append(\" index=\\\"\").append(index).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(item)) {\n            sb.append(\" item=\\\"\").append(item).append(QUOTE);\n        }\n        if (StringUtils.isNotBlank(separator)) {\n            sb.append(\" separator=\\\"\").append(separator).append(QUOTE);\n        }\n        return sb.append(RIGHT_CHEV).append(NEWLINE).append(sqlScript).append(NEWLINE).append(\"</foreach>\").toString();\n    }\n\n    /**\n     * <p>\n     * 生成 where 标签的脚本\n     * </p>\n     *\n     * @param sqlScript where 内部的 sql 脚本\n     * @return where 脚本\n     */\n    public static String convertWhere(final String sqlScript) {\n        return \"<where>\" + NEWLINE + sqlScript + NEWLINE + \"</where>\";\n    }\n\n    /**\n     * <p>\n     * 生成 set 标签的脚本\n     * </p>\n     *\n     * @param sqlScript set 内部的 sql 脚本\n     * @return set 脚本\n     */\n    public static String convertSet(final String sqlScript) {\n        return \"<set>\" + NEWLINE + sqlScript + NEWLINE + \"</set>\";\n    }\n\n    /**\n     * <p>\n     * 安全入参:  #{入参}\n     * </p>\n     *\n     * @param param 入参\n     * @return 脚本\n     */\n    public static String safeParam(final String param) {\n        return safeParam(param, null);\n    }\n\n    /**\n     * <p>\n     * 安全入参:  #{入参,mapping}\n     * </p>\n     *\n     * @param param   入参\n     * @param mapping 映射\n     * @return 脚本\n     */\n    public static String safeParam(final String param, final String mapping) {\n        String target = HASH_LEFT_BRACE + param;\n        if (StringUtils.isBlank(mapping)) {\n            return target + RIGHT_BRACE;\n        }\n        return target + COMMA + mapping + RIGHT_BRACE;\n    }\n\n    /**\n     * <p>\n     * 非安全入参:  ${入参}\n     * </p>\n     *\n     * @param param 入参\n     * @return 脚本\n     */\n    public static String unSafeParam(final String param) {\n        return DOLLAR_LEFT_BRACE + param + RIGHT_BRACE;\n    }\n\n    public static String mappingTypeHandler(Class<? extends TypeHandler<?>> typeHandler) {\n        if (typeHandler != null) {\n            return \"typeHandler=\" + typeHandler.getName();\n        }\n        return null;\n    }\n\n    public static String mappingJdbcType(JdbcType jdbcType) {\n        if (jdbcType != null) {\n            return \"jdbcType=\" + jdbcType.name();\n        }\n        return null;\n    }\n\n    public static String mappingNumericScale(Integer numericScale) {\n        if (numericScale != null) {\n            return \"numericScale=\" + numericScale;\n        }\n        return null;\n    }\n\n    public static String convertParamMapping(Class<? extends TypeHandler<?>> typeHandler, JdbcType jdbcType, Integer numericScale) {\n        if (typeHandler == null && jdbcType == null && numericScale == null) {\n            return null;\n        }\n        String mapping = null;\n        if (typeHandler != null) {\n            mapping = mappingTypeHandler(typeHandler);\n        }\n        if (jdbcType != null) {\n            mapping = appendMapping(mapping, mappingJdbcType(jdbcType));\n        }\n        if (numericScale != null) {\n            mapping = appendMapping(mapping, mappingNumericScale(numericScale));\n        }\n        return mapping;\n    }\n\n    private static String appendMapping(String mapping, String other) {\n        if (mapping != null) {\n            return mapping + COMMA + other;\n        }\n        return other;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport com.baomidou.mybatisplus.core.enums.SqlLike;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * SqlUtils工具类\n * !!! 本工具不适用于本框架外的类使用 !!!\n *\n * @author Caratacus\n * @since 2016-11-13\n */\npublic abstract class SqlUtils implements Constants {\n    private static final String tp = \"[\\\\w-,]+?\";\n    private static final Pattern pattern = Pattern.compile(String.format(\"\\\\{@((%s)|(%s:\\\\w+?)|(%s:\\\\w+?:\\\\w+?))}\", tp, tp, tp));\n    private static final Map<String, String> PLACEHOLDER_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 用%连接like\n     *\n     * @param str 原字符串\n     * @return like 的值\n     */\n    public static String concatLike(Object str, SqlLike type) {\n        switch (type) {\n            case LEFT:\n                return PERCENT + str;\n            case RIGHT:\n                return str + PERCENT;\n            default:\n                return PERCENT + str + PERCENT;\n        }\n    }\n\n    public static List<String> findPlaceholder(String sql) {\n        Matcher matcher = pattern.matcher(sql);\n        List<String> list = new ArrayList<>();\n        while (matcher.find()) {\n            list.add(matcher.group());\n        }\n        return list;\n    }\n\n    public static String replaceSqlPlaceholder(String sql, List<String> placeHolder, String escapeSymbol) {\n        for (String s : placeHolder) {\n            String body = CollectionUtils.computeIfAbsent(PLACEHOLDER_CACHE, s + escapeSymbol, s1 -> getSelectBody(s, escapeSymbol));\n            sql = sql.replace(s, body);\n        }\n        return sql;\n    }\n\n    public static String getSelectBody(String placeHolder, String escapeSymbol) {\n        String s1 = placeHolder.substring(2, placeHolder.length() - 1);\n        int i1 = s1.indexOf(COLON);\n        String tableName;\n        String alisa = null;\n        String asAlisa = null;\n        if (i1 < 0) {\n            tableName = s1;\n        } else {\n            tableName = s1.substring(0, i1);\n            s1 = s1.substring(i1 + 1);\n            i1 = s1.indexOf(COLON);\n            if (i1 < 0) {\n                alisa = s1;\n            } else {\n                alisa = s1.substring(0, i1);\n                asAlisa = s1.substring(i1 + 1);\n            }\n        }\n        return getSelectBody(tableName, alisa, asAlisa, escapeSymbol);\n    }\n\n    @SuppressWarnings(\"all\")\n    public static String getSelectBody(String tableName, String alisa, String asAlisa, String escapeSymbol) {\n        int notSel = tableName.indexOf(\"-\");\n        List<String> notSelColl = null;\n        if (notSel > 0) {\n            notSelColl = Arrays.asList(tableName.substring(notSel + 1).split(COMMA));\n            tableName = tableName.substring(0, notSel);\n        }\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);\n        Assert.notNull(tableInfo, \"can not find TableInfo Cache by \\\"%s\\\"\", tableName);\n        String s = tableInfo.chooseSelect(TableFieldInfo::isSelect, notSelColl);\n        if (alisa == null) {\n            return s;\n        }\n        return getNewSelectBody(s, alisa, asAlisa, escapeSymbol);\n    }\n\n    public static String getNewSelectBody(String selectBody, String alisa, String asAlisa, String escapeSymbol) {\n        String[] split = selectBody.split(COMMA);\n        StringBuilder sb = new StringBuilder();\n        boolean asA = asAlisa != null;\n        for (String body : split) {\n            final String sa = alisa.concat(DOT);\n            if (asA) {\n                int as = body.indexOf(AS);\n                String column;\n                String property;\n                if (as < 0) {\n                    column = body;\n                    property = StringUtils.getTargetColumn(body);\n                } else {\n                    column = body.substring(0, as);\n                    property = body.substring(as + 4);\n                    property = StringUtils.getTargetColumn(property);\n                }\n                sb.append(sa).append(column).append(AS).append(escapeColumn(asAlisa.concat(DOT).concat(property), escapeSymbol));\n            } else {\n                sb.append(sa).append(body);\n            }\n            sb.append(COMMA);\n        }\n        return sb.deleteCharAt(sb.length() - 1).toString();\n    }\n\n    private static String escapeColumn(String column, String escapeSymbol) {\n        return escapeSymbol.concat(column).concat(escapeSymbol);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/sql/StringEscape.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.sql;\n\n/**\n * StringEscape ，数据库字符串转义\n *\n * @author Caratacus\n * @since 2016-10-16\n */\npublic class StringEscape {\n\n    /**\n     * 字符串是否需要转义\n     *\n     * @param str ignore\n     * @param len ignore\n     * @return 是否需要转义\n     */\n    private static boolean isEscapeNeededForString(String str, int len) {\n        boolean needsHexEscape = false;\n        for (int i = 0; i < len; ++i) {\n            char c = str.charAt(i);\n            switch (c) {\n                /* Must be escaped for 'mysql' */\n                case 0:\n                    needsHexEscape = true;\n                    break;\n                /* Must be escaped for logs */\n                case '\\n':\n                    needsHexEscape = true;\n                    break;\n                case '\\r':\n                    needsHexEscape = true;\n                    break;\n                case '\\\\':\n                    needsHexEscape = true;\n                    break;\n                case '\\'':\n                    needsHexEscape = true;\n                    break;\n                /* Better safe than sorry */\n                case '\"':\n                    needsHexEscape = true;\n                    break;\n                /* This gives problems on Win32 */\n                case '\\032':\n                    needsHexEscape = true;\n                    break;\n                default:\n                    break;\n            }\n            if (needsHexEscape) {\n                // no need to scan more\n                break;\n            }\n        }\n        return needsHexEscape;\n    }\n\n    /**\n     * 转义字符串。纯转义，不添加单引号。\n     *\n     * @param escapeStr 被转义的字符串\n     * @return 转义后的字符串\n     */\n    public static String escapeRawString(String escapeStr) {\n        int stringLength = escapeStr.length();\n        if (isEscapeNeededForString(escapeStr, stringLength)) {\n            StringBuilder buf = new StringBuilder((int) (escapeStr.length() * 1.1));\n            //\n            // Note: buf.append(char) is _faster_ than appending in blocks,\n            // because the block append requires a System.arraycopy().... go\n            // figure...\n            //\n            for (int i = 0; i < stringLength; ++i) {\n                char c = escapeStr.charAt(i);\n                switch (c) {\n                    /* Must be escaped for 'mysql' */\n                    case 0:\n                        buf.append('\\\\');\n                        buf.append('0');\n\n                        break;\n                    /* Must be escaped for logs */\n                    case '\\n':\n                        buf.append('\\\\');\n                        buf.append('n');\n\n                        break;\n\n                    case '\\r':\n                        buf.append('\\\\');\n                        buf.append('r');\n\n                        break;\n\n                    case '\\\\':\n                        buf.append('\\\\');\n                        buf.append('\\\\');\n\n                        break;\n\n                    case '\\'':\n                        buf.append('\\\\');\n                        buf.append('\\'');\n\n                        break;\n                    /* Better safe than sorry */\n                    case '\"':\n                        buf.append('\\\\');\n                        buf.append('\"');\n\n                        break;\n                    /* This gives problems on Win32 */\n                    case '\\032':\n                        buf.append('\\\\');\n                        buf.append('Z');\n                        break;\n                    default:\n                        buf.append(c);\n                }\n            }\n            return buf.toString();\n        } else {\n            return escapeStr;\n        }\n    }\n\n    /**\n     * 转义字符串\n     *\n     * @param escapeStr 被转义的字符串\n     * @return 转义后的字符串\n     */\n    public static String escapeString(String escapeStr) {\n        if (escapeStr.matches(\"\\'(.+)\\'\")) {\n            escapeStr = escapeStr.substring(1, escapeStr.length() - 1);\n        }\n        return \"\\'\" + escapeRawString(escapeStr) + \"\\'\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/sql/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * SQL 处理相关工具类\n */\npackage com.baomidou.mybatisplus.core.toolkit.sql;\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/BiIntFunction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\n/**\n * 接受 Int 小类型的处理函数，使用小类型来避免 Java 自动装箱\n *\n * @author HCL\n * Create at 2018/11/19\n */\n@FunctionalInterface\npublic interface BiIntFunction<T, R> {\n\n    /**\n     * 函数主接口\n     *\n     * @param t 被执行类型 T\n     * @param i 参数\n     * @return 返回\n     */\n    R apply(T t, int i);\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ColumnCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * @author miemie\n * @since 2018-12-30\n */\n@Data\n@AllArgsConstructor\npublic class ColumnCache implements Serializable {\n\n    private static final long serialVersionUID = -4586291538088403456L;\n\n    /**\n     * 使用 column\n     */\n    private String column;\n    /**\n     * 查询 column\n     */\n    private String columnSelect;\n    /**\n     * mapping\n     */\n    private String mapping;\n\n    public ColumnCache(String column, String columnSelect) {\n        this.column = column;\n        this.columnSelect = columnSelect;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/IdeaProxyLambdaMeta.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandleProxies;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Proxy;\n\n/**\n * 在 IDEA 的 Evaluate 中执行的 Lambda 表达式元数据需要使用该类处理元数据\n * <p>\n *\n * @author HCL\n * Create at 2021/5/17\n */\npublic class IdeaProxyLambdaMeta implements LambdaMeta {\n    private final Class<?> clazz;\n    private final String name;\n\n    public IdeaProxyLambdaMeta(Proxy func) {\n        MethodHandle dmh = MethodHandleProxies.wrapperInstanceTarget(func);\n        Executable executable = MethodHandles.reflectAs(Executable.class, dmh);\n        clazz = executable.getDeclaringClass();\n        name = executable.getName();\n    }\n\n    @Override\n    public String getImplMethodName() {\n        return name;\n    }\n\n    @Override\n    public Class<?> getInstantiatedClass() {\n        return clazz;\n    }\n\n    @Override\n    public String toString() {\n        return clazz.getSimpleName() + \"::\" + name;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/LambdaMeta.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\n/**\n * Lambda 信息\n * <p>\n *\n * @author HCL\n * Created at 2021/5/14\n */\npublic interface LambdaMeta {\n\n    /**\n     * 获取 lambda 表达式实现方法的名称\n     *\n     * @return lambda 表达式对应的实现方法名称\n     */\n    String getImplMethodName();\n\n    /**\n     * 实例化该方法的类\n     *\n     * @return 返回对应的类名称\n     */\n    Class<?> getInstantiatedClass();\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ReflectLambdaMeta.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.invoke.SerializedLambda;\n\n/**\n * @author HCL\n * Created at 2021/5/14\n */\n@Slf4j\npublic class ReflectLambdaMeta implements LambdaMeta {\n    private final SerializedLambda lambda;\n\n    private final ClassLoader classLoader;\n\n    public ReflectLambdaMeta(SerializedLambda lambda, ClassLoader classLoader) {\n        this.lambda = lambda;\n        this.classLoader = classLoader;\n    }\n\n    @Override\n    public String getImplMethodName() {\n        return lambda.getImplMethodName();\n    }\n\n    @Override\n    public Class<?> getInstantiatedClass() {\n        String instantiatedMethodType = lambda.getInstantiatedMethodType();\n        String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)).replace(StringPool.SLASH, StringPool.DOT);\n        return ClassUtils.toClassConfident(instantiatedType, this.classLoader);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/SFunction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\nimport java.io.Serializable;\nimport java.util.function.Function;\n\n/**\n * 支持序列化的 Function\n *\n * @author miemie\n * @since 2018-05-12\n */\n@FunctionalInterface\npublic interface SFunction<T, R> extends Function<T, R>, Serializable {\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/SerializedLambda.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.ObjectStreamClass;\nimport java.io.Serializable;\n\n/**\n * 当前类是 {@link java.lang.invoke.SerializedLambda } 的一个镜像\n * <p>\n *\n * @author HCL\n * Create at 2020/7/17\n */\n@SuppressWarnings(\"ALL\")\npublic class SerializedLambda implements Serializable {\n    private static final long serialVersionUID = 8025925345765570181L;\n\n    private Class<?> capturingClass;\n    private String functionalInterfaceClass;\n    private String functionalInterfaceMethodName;\n    private String functionalInterfaceMethodSignature;\n    private String implClass;\n    private String implMethodName;\n    private String implMethodSignature;\n    private int implMethodKind;\n    private String instantiatedMethodType;\n    private Object[] capturedArgs;\n\n    public static SerializedLambda extract(Serializable serializable) {\n        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();\n             ObjectOutputStream oos = new ObjectOutputStream(baos)) {\n            oos.writeObject(serializable);\n            oos.flush();\n            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) {\n                @Override\n                protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {\n                    Class<?> clazz = super.resolveClass(desc);\n                    return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;\n                }\n\n            }) {\n                return (SerializedLambda) ois.readObject();\n            }\n        } catch (IOException | ClassNotFoundException e) {\n            throw new MybatisPlusException(e);\n        }\n    }\n\n    public String getInstantiatedMethodType() {\n        return instantiatedMethodType;\n    }\n\n    public Class<?> getCapturingClass() {\n        return capturingClass;\n    }\n\n    public String getImplMethodName() {\n        return implMethodName;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ShadowLambdaMeta.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.core.toolkit.support;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\n\n/**\n * 基于 {@link SerializedLambda} 创建的元信息\n * <p>\n *\n * @author HCL\n * Create at 2021/7/7\n */\npublic class ShadowLambdaMeta implements LambdaMeta {\n    private final SerializedLambda lambda;\n\n    public ShadowLambdaMeta(SerializedLambda lambda) {\n        this.lambda = lambda;\n    }\n\n    @Override\n    public String getImplMethodName() {\n        return lambda.getImplMethodName();\n    }\n\n    @Override\n    public Class<?> getInstantiatedClass() {\n        String instantiatedMethodType = lambda.getInstantiatedMethodType();\n        String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)).replace(StringPool.SLASH, StringPool.DOT);\n        return ClassUtils.toClassConfident(instantiatedType, lambda.getCapturingClass().getClassLoader());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/main/resources/META-INF/spring-devtools.properties",
    "content": "restart.include.mybatis-plus=/mybatis-plus-[\\\\w-]+\\.jar\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/code/Overwrite.java",
    "content": "package com.baomidou.mybatisplus.code;\n\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.Singular;\n\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2025/9/1\n */\n@Data\n@Builder\npublic class Overwrite {\n    private String front;\n    private String behind;\n    private int interval;\n    /*  */\n    @Singular\n    private List<Content> contents;\n    @Singular(\"addImport\")\n    private List<String> imports;\n\n    @Data\n    @Builder\n    public static class Content {\n        @Builder.Default\n        private Operate operate = Operate.INSERT;\n        private int frontDown;\n        private int behindUp;\n        private String code;\n    }\n\n    enum Operate {\n        INSERT,\n        DELETE\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/code/OverwriteFile.java",
    "content": "package com.baomidou.mybatisplus.code;\n\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\n\n/**\n * @author miemie\n * @since 2025/9/1\n */\n@Data\npublic abstract class OverwriteFile {\n\n    private List<Overwrite> steps = new ArrayList<>();\n\n    public void addStep(Function<Overwrite.OverwriteBuilder, Overwrite.OverwriteBuilder> function) {\n        this.steps.add(function.apply(Overwrite.builder()).build());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/code/OverwriteRunner.java",
    "content": "package com.baomidou.mybatisplus.code;\n\nimport com.baomidou.mybatisplus.code.sub.MapperMethod;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * @author miemie\n * @since 2025/9/1\n */\npublic class OverwriteRunner {\n\n    public static void main(String[] args) throws Exception {\n        List<OverwriteFile> files = List.of(new MapperMethod());\n        Map<String, OverwriteFile> map = finJar(files);\n        map.forEach((k, v) -> {\n            System.out.println(k);\n        });\n    }\n\n    private static Map<String, OverwriteFile> finJar(List<OverwriteFile> files) throws IOException {\n        Map<String, OverwriteFile> map = files.stream().collect(Collectors.toMap(i -> i.getClass().getSimpleName(), i -> i));\n        Map<String, OverwriteFile> result = new HashMap<>();\n        String ver = findVer();\n        Path jarPath = Paths.get(System.getProperty(\"user.home\"), \".m2\", \"repository\", \"org\", \"mybatis\", \"mybatis\",\n            ver, \"mybatis-\" + ver + \"-sources.jar\");\n        try (JarFile jarFile = new JarFile(jarPath.toFile())) {\n            for (JarEntry entry : jarFile.stream().toList()) {\n                String name = entry.getName();\n                if (!name.endsWith(\".java\")) {\n                    continue;\n                }\n                String className = name.substring(name.lastIndexOf(\"/\") + 1, name.length() - 5);\n                if (map.containsKey(className)) {\n                    result.put(name, map.get(className));\n                }\n            }\n        }\n        return result;\n    }\n\n    private static String findVer() throws IOException {\n        String userDir = System.getProperty(\"user.dir\");\n        String content = new String(Files.readAllBytes(Paths.get(userDir + \"/build.gradle\")));\n        // 定义正则表达式，匹配 mybatisVersion = '3.5.19'\n        Pattern pattern = Pattern.compile(\"mybatisVersion\\\\s*=\\\\s*['\\\"]([\\\\d.]+)['\\\"]\");\n        Matcher matcher = pattern.matcher(content);\n        if (matcher.find()) {\n            return matcher.group(1);\n        }\n        throw new RuntimeException();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/code/sub/MapperMethod.java",
    "content": "package com.baomidou.mybatisplus.code.sub;\n\nimport com.baomidou.mybatisplus.code.Overwrite;\nimport com.baomidou.mybatisplus.code.OverwriteFile;\n\n/**\n * {@link org.apache.ibatis.binding.MapperMethod}\n *\n * @author miemie\n * @since 2025/9/1\n */\npublic class MapperMethod extends OverwriteFile {\n\n    public MapperMethod() {\n        // import\n        addStep(i -> i\n            .addImport(\"com.baomidou.mybatisplus.core.metadata.IPage\")\n            .addImport(\"com.baomidou.mybatisplus.core.toolkit.Assert\"));\n        // execute\n        addStep(i -> i\n            .front(\"result = executeForCursor(sqlSession, args);\")\n            .interval(8)\n            .behind(\"case FLUSH:\")\n            .content(Overwrite.Content.builder()\n                .code(\"\"\"\n                    if (IPage.class.isAssignableFrom(method.getReturnType())) {\n                        result = executeForIPage(sqlSession, args);\n                    } else {\n                    \"\"\")\n                .frontDown(1).build())\n            .content(Overwrite.Content.builder()\n                .code(\"}\")\n                .behindUp(3)\n                .build())\n        );\n        addStep(i -> i\n            .behind(\"private Object rowCountResult(int rowCount) {\")\n            .content(Overwrite.Content.builder()\n                .code(\"\"\"\n                    @SuppressWarnings(\"all\")\n                    private <E> Object executeForIPage(SqlSession sqlSession, Object[] args) {\n                        IPage<E> result = null;\n                        for (Object arg : args) {\n                            if (arg instanceof IPage) {\n                                result = (IPage<E>) arg;\n                                break;\n                            }\n                        }\n                        Assert.notNull(result, \"can't found IPage for args!\");\n                        Object param = method.convertArgsToSqlCommandParam(args);\n                        List<E> list = sqlSession.selectList(command.getName(), param);\n                        result.setRecords(list);\n                        return result;\n                    }\n                    \"\"\")\n                .behindUp(2).build())\n        );\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/MethodTest.java",
    "content": "package com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.injector.methods.*;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport static java.util.stream.Collectors.toList;\n\npublic class MethodTest {\n\n    static class TestMethod extends AbstractMethod {\n\n        public TestMethod(){\n            this(\"TestMethod\");\n        }\n        public TestMethod(String methodName) {\n            super(methodName);\n        }\n\n        @Override\n        public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n            return null;\n        }\n\n    }\n\n    @Test\n    void test(){\n        Stream.Builder<AbstractMethod> builder = Stream.<AbstractMethod>builder()\n            .add(new Insert())\n            .add(new Delete())\n            .add(new Update())\n            .add(new SelectPage())\n            .add(new TestMethod());\n        List<AbstractMethod> collect = builder.build().toList();\n        Assert.isTrue(collect.size() == 5, \"创建失败！\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilderTest.java",
    "content": "package com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.ibatis.annotations.CacheNamespace;\nimport org.apache.ibatis.annotations.CacheNamespaceRef;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author miemie\n * @since 2020-11-07\n */\nclass MybatisMapperAnnotationBuilderTest {\n\n    @Test\n    void parse() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        MybatisMapperAnnotationBuilder a = new MybatisMapperAnnotationBuilder(configuration, AMapper.class);\n        a.parse();\n        MybatisMapperAnnotationBuilder b = new MybatisMapperAnnotationBuilder(configuration, BMapper.class);\n        b.parse();\n        configuration.getMappedStatement(AMapper.class.getName() + \".insert\");\n    }\n\n    @CacheNamespaceRef(BMapper.class)\n    interface AMapper extends BaseMapper<A> {\n\n    }\n\n    @CacheNamespace\n    interface BMapper extends BaseMapper<B> {\n\n    }\n\n    @Data\n    private static class A {\n        private Long id;\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    private static class B extends A {\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/MybatisXMLConfigBuilderTest.java",
    "content": "package com.baomidou.mybatisplus.core;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport lombok.Data;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.core.io.DefaultResourceLoader;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\n\nimport java.io.IOException;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-15\n */\nclass MybatisXMLConfigBuilderTest {\n\n    @Test\n    void parse() throws IOException {\n        ResourceLoader loader = new DefaultResourceLoader();\n        Resource resource = loader.getResource(\"classpath:/MybatisXMLConfigBuilderTest.xml\");\n        MybatisXMLConfigBuilder builder = new MybatisXMLConfigBuilder(resource.getInputStream(), null);\n        Configuration configuration = builder.parse();\n        assertThat(configuration).isInstanceOf(MybatisConfiguration.class);\n        assertThat(configuration.getMappedStatement(\"com.baomidou.mybatisplus.core.MybatisXMLConfigBuilderTest$EntityMapper.selectCount\"))\n            .isNotNull();\n    }\n\n    interface EntityMapper extends BaseMapper<Entity> {\n\n    }\n\n    @Data\n    @TableName(autoResultMap = true)\n    static class Entity {\n        private Long id;\n        private String name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/MybatisXMLLanguageDriverTest.java",
    "content": "package com.baomidou.mybatisplus.core;\n\nimport org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@Disabled\npublic class MybatisXMLLanguageDriverTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisXMLLanguageDriver.class);\n\n    @Test\n    void test1() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <if test=\"true\">\n                                    1=1\n                                </if>\n                                and 2=2\n                                <if test=\"true\">\n                                    and 3=3\n                                </if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE 1=1  and 2=2  and 3=3\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <if test=\"false\">\n                                    1=1\n                                </if>\n                                and 2=2\n                                <if test=\"true\">\n                                    and 3=3\n                                </if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  2=2  and 3=3\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <if test=\"false\">\n                                    1=1\n                                </if>\n                                and 2=2\n                                <if test=\"true\">\n                                    and 3=3\n                                </if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  2=2  and 3=3\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <if test=\"true\">1=1</if>and 2=2<if test=\"true\">and 3=3</if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE 1=1and 2=2and 3=3\");\n\n    }\n\n    @Test\n    void test5() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            where 1=1\n                            <if test=\"1==1\">and id is not null</if>\n                            <if test=\"1==1\">and name is not null</if>\n                        </script>\n            \"\"\", \"select * from `user`\\n\" +\n            \"                where 1=1  and id is not null   and name is not null\");\n    }\n\n    @Test\n    void test6() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            where 1=1\n                            <if test=\"1==1\">and id is not null</if><if test=\"1==1\">and name is not null</if>\n                        </script>\n            \"\"\", \"select * from `user`\\n\" +\n            \"                where 1=1  and id is not null and name is not null\");\n    }\n\n    @Test\n    void test7() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                            <if test=\"1==1\">and id is not null</if><if test=\"1==1\">and name is not null</if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  id is not nulland name is not null\");\n    }\n\n    @Test\n    void test8() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                            <if test=\"1==1\">and id is not null</if>\n                            <if test=\"1==1\">and name is not null</if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  id is not null and name is not null\");\n    }\n\n    @Test\n    void test9() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                            <if test=\"1==1\">and id is not null</if>\n                            <if test=\"1==1\">and name is not null</if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  id is not null and name is not null\");\n    }\n\n    @Test\n    void test10() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user` where 1 = 1\n                            <if test=\"1==1\">and id is not null</if><if test=\"1==1\">and name is not null</if>\n                        </script>\n            \"\"\", \"select * from `user` where 1 = 1  and id is not null and name is not null\");\n    }\n\n    @Test\n    void test11() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user` where 1 = 1\n                            <if test=\"1==1\">and id is not null</if>\n                            <if test=\"1==1\">and name is not null</if>\n                        </script>\n            \"\"\", \"select * from `user` where 1 = 1  and id is not null   and name is not null\");\n    }\n\n    @Test\n    void test12() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user` where 1 = 1 and id in\n                            <foreach collection='@java.util.Arrays@asList(1,2,3,4,5)' item='item' separator=',' open='(' close=')'>\n                                #{item}\n                            </foreach>\n                        </script>\n            \"\"\", \"select * from `user` where 1 = 1 and id in  (   ?  ,  ?  ,  ?  ,  ?  ,  ?  )\");\n    }\n\n    @Test\n    void test13() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user` where 1 = 1 and\n                            <foreach collection='@java.util.Arrays@asList(1,2,3,4,5)' item='item' separator='and'>\n                                <if test=\"item == 1\">id is not null</if>\n                                <if test=\"item == 2\">name is not null</if>\n                                <if test=\"item == 3\">age is not null</if>\n                            </foreach>\n                        </script>\n            \"\"\", \"select * from `user` where 1 = 1 and     id is not null           and name is not null           and age is not null\");\n    }\n\n    @Test\n    void test14() {\n        assertSql(\"\"\"\n            <script>\n                   update user\n                   <set>\n                         <if test=\"true\">username=#{username},</if>\n                         <if test=\"false\">password=#{password},</if>\n                         <if test=\"true\">email=#{email},</if>\n                         <if test=\"true\">bio=#{bio},</if>\n                       </set>\n                     where id=#{id}\n            </script>\n            \"\"\", \"update user  SET username=?,  email=?, bio=?  where id=?\");\n    }\n\n    @Test\n    void test15() {\n        assertSql(\"\"\"\n            <script>\n                   update user\n                   <!--这是一条更新语句-->\n                   <set>\n                         <if test=\"true\">username=#{username},</if>\n                         <if test=\"false\">password=#{password},</if>\n                         <if test=\"true\">email=#{email},</if>\n                         <if test=\"true\">bio=#{bio},</if>\n                       </set>\n                     where id=#{id}\n            </script>\n            \"\"\", \"update user  SET username=?,  email=?, bio=?  where id=?\");\n    }\n\n    @Test\n    void test16() {\n        assertSql(\"\"\"\n            <script>\n                   update user\n                   <!--这是一条更新语句-->\n                   <bind name=\"name\" value=\"test\" />\n                   <set>\n                         <if test=\"true\">username=#{username},</if>\n                         <if test=\"false\">password=#{password},</if>\n                         <if test=\"true\">email=#{email},</if>\n                         <if test=\"true\">bio=#{bio},</if>\n                       </set>\n                     where id=#{id}\n            </script>\n            \"\"\", \"update user    SET username=?,  email=?, bio=?  where id=?\");\n    }\n\n    @Test\n    void test17() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                            <!--\n                                 查了点东西啊\n                                 12345\n                                 789\n                            -->\n                                <if test=\"true\">1=1</if>and 2=2<if test=\"true\">and 3=3</if>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE 1=1and 2=2and 3=3\");\n    }\n\n    @Test\n    void test18() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <choose>\n                                    <when test=\"false\">\n                                        and age > #{age}\n                                    </when>\n                                    <when test=\"false\">\n                                        and name like concat(#{name},'%')\n                                    </when>\n                                    <otherwise>\n                                        and sex = '男'\n                                    </otherwise>\n                                </choose>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  sex = '男'\");\n    }\n\n    @Test\n    void test19() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <choose>\n                                    <when test=\"false\">\n                                        and age > #{age}\n                                    </when>\n                                    <when test=\"true\">\n                                        and name like concat(#{name},'%')\n                                    </when>\n                                    <otherwise>\n                                        and sex = '男'\n                                    </otherwise>\n                                </choose>\n                            </where>\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  name like concat(?,'%')\");\n    }\n\n    @Test\n    void test20() {\n        assertSql(\"\"\"\n            <script>\n                            select * from `user`\n                            <where>\n                                <choose>\n                                    <when test=\"false\">\n                                        and age > #{age}\n                                    </when>\n                                    <when test=\"true\">and name like concat(#{name},'%')</when>\n                                    <otherwise>\n                                        and sex = '男'\n                                    </otherwise>\n                                </choose>\n                            </where>\n                            and 1=1\n                        </script>\n            \"\"\", \"select * from `user`  WHERE  name like concat(?,'%')  and 1=1\");\n    }\n\n\n\n    void runMybatis(String script, String sql) {\n        var languageDriver = new XMLLanguageDriver();\n        var sqlSource = languageDriver.createSqlSource(new MybatisConfiguration(), script, Object.class);\n        LOGGER.info(\"mybatis parse :{}\", sqlSource.getBoundSql(null).getSql());\n    }\n\n\n    void assertSql(String script, String sql) {\n        var languageDriver = new MybatisXMLLanguageDriver();\n        var sqlSource = languageDriver.createSqlSource(new MybatisConfiguration(), script, Object.class);\n        runMybatis(script, sql);\n        var boundSql = sqlSource.getBoundSql(null).getSql();\n        LOGGER.info(\"mybatis-plus parse :{}\", boundSql);\n        Assertions.assertEquals(sql, boundSql);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/MybatisXMLScriptBuilderTest.java",
    "content": "package com.baomidou.mybatisplus.core;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport static com.baomidou.mybatisplus.core.MybatisXMLScriptBuilder.replaceLeadingAndTrailingWhitespace;\n\npublic class MybatisXMLScriptBuilderTest {\n\n    @Test\n    void testReplaceLeadingAndTrailingWhitespace() {\n        Assertions.assertEquals(\" 1=1 \", replaceLeadingAndTrailingWhitespace(\"\\n    1=1 \\n \\n\\n\"));\n        Assertions.assertEquals(\" 1=1 \", replaceLeadingAndTrailingWhitespace(\"\\t\\t1=1 \\n \\n\\n\"));\n        Assertions.assertEquals(\" 1=1 \", replaceLeadingAndTrailingWhitespace(\"    1=1 \\n \\n\\n\"));\n        Assertions.assertEquals(\"1=1 \", replaceLeadingAndTrailingWhitespace(\"1=1 \\n \\n\\n\"));\n        Assertions.assertEquals(\" 1\\n=1 \", replaceLeadingAndTrailingWhitespace(\"\\n    1\\n=1 \\n \\n\\n\"));\n        Assertions.assertEquals(\"1\\n=1 \", replaceLeadingAndTrailingWhitespace(\"1\\n=1 \\n \\n\\n\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/AbstractWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2023-08-01\n */\nclass AbstractWrapperTest {\n\n    @Test\n    void formatSqlMaybeWithParam() {\n        QueryWrapper<Object> wrapper = new QueryWrapper<>();\n        String s = wrapper.formatSqlMaybeWithParam(\"c={0}\", 1);\n        assertThat(s).isEqualTo(\"c=#{ew.paramNameValuePairs.MPGENVAL1}\");\n\n        s = wrapper.formatSqlMaybeWithParam(\"c={0,javaType=int}\", 1);\n        assertThat(s).isEqualTo(\"c=#{ew.paramNameValuePairs.MPGENVAL2,javaType=int}\");\n\n        s = wrapper.formatSqlMaybeWithParam(\"c={0,javaType=int} and b={1,jdbcType=NUMERIC} pp\", 1, 2);\n        assertThat(s).isEqualTo(\"c=#{ew.paramNameValuePairs.MPGENVAL3,javaType=int} \" +\n            \"and b=#{ew.paramNameValuePairs.MPGENVAL4,jdbcType=NUMERIC} pp\");\n\n        s = wrapper.formatSqlMaybeWithParam(\"c={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler} pp\", 1);\n        assertThat(s).isEqualTo(\"c=#{ew.paramNameValuePairs.MPGENVAL5,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler} pp\");\n\n        Exception ex = null;\n        try {\n            wrapper.formatSqlMaybeWithParam(\"c={1} pp\", 1);\n        } catch (Exception e) {\n            ex = e;\n        }\n        assertThat(ex).isNotNull();\n\n        try {\n            wrapper.formatSqlMaybeWithParam(\"c={1}\", 1);\n        } catch (Exception e) {\n            ex = e;\n        }\n        assertThat(ex).isNotNull();\n        System.out.println(ex.getMessage());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/BaseWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.Data;\nimport org.assertj.core.api.Assertions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\nclass BaseWrapperTest {\n\n    void logParams(AbstractWrapper<?, ?, ?> wrapper) {\n        wrapper.getParamNameValuePairs().forEach((k, v) -> {\n            System.out.println(\"key: '\" + k + \"'\\t\\tvalue: '\" + v + StringPool.SINGLE_QUOTE);\n            assertThat(k).startsWith(Constants.WRAPPER_PARAM);\n        });\n    }\n\n    void logSqlSet(String explain, AbstractWrapper<?, ?, ?> wrapper, String targetSql) {\n        System.out.printf(\" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓%n\", explain);\n        System.out.println(wrapper.getSqlSet());\n        Assertions.assertThat(wrapper.getSqlSet().trim()).isEqualTo(targetSql);\n    }\n\n    void logSqlWhere(String explain, AbstractWrapper<?, ?, ?> wrapper, String targetSql) {\n        System.out.printf(\" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓%n\", explain);\n        System.out.println(wrapper.getSqlSegment());\n        assertThat(wrapper.getTargetSql().trim()).isEqualTo(targetSql);\n    }\n\n    @Data\n    protected static class Entity {\n        private Integer id;\n\n        @TableField(\"username\")\n        private String name;\n\n        private Integer roleId;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/LambdaQueryWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport lombok.Data;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\n/**\n * LambdaQueryWrapper 测试\n *\n * @author miemie\n * @since 2021-01-27\n */\nclass LambdaQueryWrapperTest extends BaseWrapperTest {\n\n    @BeforeAll\n    static void initTableInfo() {\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), Table.class);\n        Assertions.assertEquals(\"xxx\", tableInfo.getTableName());\n    }\n\n    @Test\n    void testLambdaOrderBySqlSegment() {\n        LambdaQueryWrapper<?> lqw = Wrappers.<Table>lambdaQuery().orderByDesc(Table::getId);\n        Assertions.assertEquals(\" ORDER BY `id` DESC\", lqw.getSqlSegment());\n        lqw.clear();\n        Assertions.assertEquals(\"\", lqw.getSqlSegment());\n        lqw = Wrappers.<Table>lambdaQuery().eq(Table::getId, 1).nested(false, x -> x.eq(Table::getName, \"李白\"));\n        Assertions.assertEquals(\"(`id` = #{ew.paramNameValuePairs.MPGENVAL1})\", lqw.getSqlSegment());\n    }\n\n\n    @Data\n    @TableName(\"xxx\")\n    private static class Table {\n\n        @TableId(\"`id`\")\n        private Long id;\n\n        @TableField(\"`name`\")\n        private Long name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/QueryWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.util.*;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\nclass QueryWrapperTest extends BaseWrapperTest {\n\n    @Test\n    void testCacheSqlSegment() {\n        QueryWrapper<Entity> ew = new QueryWrapper<Entity>()\n            .eq(\"xxx\", 123)\n            .and(i -> i.eq(\"andx\", 65444).le(\"ande\", 66666))\n            .ne(\"xxx\", 222);\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ?)\");\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ?)\");\n        ew.gt(\"x22\", 333);\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ? AND x22 > ?)\");\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ? AND x22 > ?)\");\n        ew.orderByAsc(\"column\");\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ? AND x22 > ?) ORDER BY column ASC\");\n        logSqlWhere(\"xx\", ew, \"(xxx = ? AND (andx = ? AND ande <= ?) AND xxx <> ? AND x22 > ?) ORDER BY column ASC\");\n        logParams(ew);\n    }\n\n    @Test\n    void testQueryWrapper() {\n        logSqlWhere(\"去除第一个 or,以及自动拼接 and,以及手动拼接 or,以及去除最后的多个or\", new QueryWrapper<Entity>().or()\n                .ge(\"age\", 3).or().ge(\"age\", 3).ge(\"age\", 3).or().or().or().or(),\n            \"(age >= ? OR age >= ? AND age >= ?)\");\n\n        logSqlWhere(\"多个 or 相连接,去除多余的 or\", new QueryWrapper<Entity>()\n                .ge(\"age\", 3).or().or().or().ge(\"age\", 3).or().or().ge(\"age\", 3),\n            \"(age >= ? OR age >= ? OR age >= ?)\");\n\n        logSqlWhere(\"嵌套,正常嵌套\", new QueryWrapper<Entity>()\n                .nested(i -> i.eq(\"id\", 1)).eq(\"id\", 1),\n            \"((id = ?) AND id = ?)\");\n\n        logSqlWhere(\"嵌套,第一个套外的 and 自动消除\", new QueryWrapper<Entity>()\n                .and(i -> i.eq(\"id\", 1)).eq(\"id\", 1),\n            \"((id = ?) AND id = ?)\");\n\n        logSqlWhere(\"嵌套,多层嵌套\", new QueryWrapper<Entity>()\n                .and(i -> i.eq(\"id\", 1).and(j -> j.eq(\"id\", 2))),\n            \"((id = ? AND (id = ?)))\");\n\n        logSqlWhere(\"嵌套,第一个套外的 or 自动消除\", new QueryWrapper<Entity>()\n                .or(i -> i.eq(\"id\", 1)).eq(\"id\", 1),\n            \"((id = ?) AND id = ?)\");\n\n        logSqlWhere(\"嵌套,套内外自动拼接 and\", new QueryWrapper<Entity>()\n                .eq(\"id\", 11).and(i -> i.eq(\"id\", 1)).eq(\"id\", 1),\n            \"(id = ? AND (id = ?) AND id = ?)\");\n\n        logSqlWhere(\"嵌套,套内外手动拼接 or,去除套内第一个 or\", new QueryWrapper<Entity>()\n                .eq(\"id\", 11).or(i -> i.or().eq(\"id\", 1)).or().eq(\"id\", 1),\n            \"(id = ? OR (id = ?) OR id = ?)\");\n\n        logSqlWhere(\"多个 order by 和 group by 拼接,自动优化顺序,last方法拼接在最后\", new QueryWrapper<Entity>()\n                .eq(\"id\", 11)\n                .last(\"limit 1\")\n                .orderByAsc(\"id\", \"name\", \"sex\").orderByDesc(\"age\", \"txl\")\n                .groupBy(\"id\", \"name\", \"sex\").groupBy(\"id\", \"name\"),\n            \"(id = ?) GROUP BY id,name,sex,id,name ORDER BY id ASC,name ASC,sex ASC,age DESC,txl DESC limit 1\");\n\n        logSqlWhere(\"只存在 order by\", new QueryWrapper<Entity>()\n                .orderByAsc(\"id\", \"name\", \"sex\").orderByDesc(\"age\", \"txl\"),\n            \"ORDER BY id ASC,name ASC,sex ASC,age DESC,txl DESC\");\n\n        logSqlWhere(\"只存在 group by\", new QueryWrapper<Entity>()\n                .groupBy(\"id\", \"name\", \"sex\").groupBy(\"id\", \"name\"),\n            \"GROUP BY id,name,sex,id,name\");\n    }\n\n    @Test\n    void testCompare() {\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<Entity>()\n            .allEq(getMap()).allEq((k, v) -> true, getMap())\n            .eq(\"id\", 1).ne(\"id\", 1)\n            .or().gt(\"id\", 1).ge(\"id\", 1)\n            .lt(\"id\", 1).le(\"id\", 1)\n            .or().between(\"id\", 1, 2).notBetween(\"id\", 1, 3)\n            .like(\"id\", 1).notLike(\"id\", 1).notLikeLeft(\"id\", 3).notLikeRight(\"id\", 4)\n            .or().likeLeft(\"id\", 1).likeRight(\"id\", 1);\n        logSqlWhere(\"测试 Compare 下的方法\", queryWrapper, \"(column1 = ? AND column0 = ? AND nullColumn IS NULL AND column1 = ? AND column0 = ? AND nullColumn IS NULL AND id = ? AND id <> ? OR id > ? AND id >= ? AND id < ? AND id <= ? OR id BETWEEN ? AND ? AND id NOT BETWEEN ? AND ? AND id LIKE ? AND id NOT LIKE ? AND id NOT LIKE ? AND id NOT LIKE ? OR id LIKE ? AND id LIKE ?)\");\n        logParams(queryWrapper);\n    }\n\n    @Test\n    void testFunc() {\n        Entity entity = new Entity();\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<Entity>()\n            .isNull(\"nullColumn\").or().isNotNull(\"notNullColumn\")\n            .orderByAsc(\"id\").orderByDesc(\"name\", \"name2\")\n            .groupBy(\"id\").groupBy(\"name\", \"id2\", \"name2\")\n            .in(\"inColl\", getList()).or().notIn(\"notInColl\", getList())\n            .in(\"inArray\").notIn(\"notInArray\", 5, 6, 7)\n            .eqSql(\"eqSql\", \"1\")\n            .inSql(\"inSql\", \"1,2,3,4,5\").notInSql(\"inSql\", \"1,2,3,4,5\")\n            .gtSql(\"gtSql\", \"1,2,3,4,5\").ltSql(\"ltSql\", \"1,2,3,4,5\")\n            .geSql(\"geSql\", \"1,2,3,4,5\").leSql(\"leSql\", \"1,2,3,4,5\")\n            .having(\"sum(age) > {0}\", 1).having(\"id is not null\")\n            .func(entity.getId() != null, j -> j.eq(\"id\", entity.getId()));// 不会npe,也不会加入sql\n        logSqlWhere(\"测试 Func 下的方法\", queryWrapper, \"(nullColumn IS NULL OR notNullColumn IS NOT NULL AND inColl IN (?,?) OR notInColl NOT IN (?,?) AND inArray IN () AND notInArray NOT IN (?,?,?) AND eqSql = (1) AND inSql IN (1,2,3,4,5) AND inSql NOT IN (1,2,3,4,5) AND gtSql > (1,2,3,4,5) AND ltSql < (1,2,3,4,5) AND geSql >= (1,2,3,4,5) AND leSql <= (1,2,3,4,5)) GROUP BY id,name,id2,name2 HAVING sum(age) > ? AND id is not null ORDER BY id ASC,name DESC,name2 DESC\");\n        logParams(queryWrapper);\n    }\n\n    @Test\n    void testJoin() {\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<Entity>()\n            .last(\"limit 1\").or()\n            .apply(\"date_format(column,'%Y-%m-%d') = '2008-08-08'\")\n            .apply(\"date_format(column,'%Y-%m-%d') = {0}\", LocalDate.now())\n            .or().exists(\"select id from table where age = 1\")\n            .or().notExists(\"select id from table where age = 1\");\n        logSqlWhere(\"测试 Join 下的方法\", queryWrapper, \"(date_format(column,'%Y-%m-%d') = '2008-08-08' AND date_format(column,'%Y-%m-%d') = ? OR EXISTS (select id from table where age = 1) OR NOT EXISTS (select id from table where age = 1)) limit 1\");\n        logParams(queryWrapper);\n    }\n\n    @Test\n    void testNested() {\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<Entity>()\n            .and(i -> i.eq(\"id\", 1).nested(j -> j.ne(\"id\", 2)))\n            .or(i -> i.eq(\"id\", 1).and(j -> j.ne(\"id\", 2)))\n            .nested(i -> i.eq(\"id\", 1).or(j -> j.ne(\"id\", 2)))\n            .not(i -> i.eq(\"id\", 1).or(j -> j.ne(\"id\", 2)));\n        logSqlWhere(\"测试 Nested 下的方法\", queryWrapper, \"((id = ? AND (id <> ?)) OR (id = ? AND (id <> ?)) AND (id = ? OR (id <> ?)) AND NOT (id = ? OR (id <> ?)))\");\n        logParams(queryWrapper);\n    }\n\n    @Test\n    void testPluralLambda() {\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), Entity.class);\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<>();\n        queryWrapper.lambda().eq(Entity::getName, \"sss\");\n        queryWrapper.lambda().eq(Entity::getName, \"sss2\");\n        logSqlWhere(\"测试 PluralLambda\", queryWrapper, \"(username = ? AND username = ?)\");\n        logParams(queryWrapper);\n    }\n\n    @Test\n    void testInEmptyColl() {\n        QueryWrapper<Entity> queryWrapper = new QueryWrapper<Entity>().in(\"xxx\", Collections.emptyList());\n        logSqlWhere(\"测试 empty 的 coll\", queryWrapper, \"(xxx IN ())\");\n    }\n\n    @Test\n    void testExistsValue() {\n        QueryWrapper<Entity> wrapper = new QueryWrapper<Entity>().eq(\"a\", \"b\")\n            .exists(\"select 1 from xxx where id = {0} and name = {1}\", 1, \"Bob\");\n        logSqlWhere(\"testExistsValue\", wrapper, \"(a = ? AND EXISTS (select 1 from xxx where id = ? and name = ?))\");\n        logParams(wrapper);\n        wrapper = new QueryWrapper<Entity>().eq(\"a\", \"b\")\n            .notExists(\"select 1 from xxx where id = {0} and name = {1}\", 1, \"Bob\");\n        logSqlWhere(\"testNotExistsValue\", wrapper, \"(a = ? AND NOT EXISTS (select 1 from xxx where id = ? and name = ?))\");\n        logParams(wrapper);\n    }\n\n    @Test\n    void testCheckSqlInjection() {\n        QueryWrapper<Entity> qw = new QueryWrapper<Entity>().checkSqlInjection().eq(\"a\", \"b\");\n        Assertions.assertEquals(\"WHERE (a = #{ew.paramNameValuePairs.MPGENVAL1})\", qw.getCustomSqlSegment());\n\n        qw.orderByAsc(\"select 1 from xxx\");\n        Assertions.assertThrows(MybatisPlusException.class, qw::getCustomSqlSegment);\n    }\n\n    @Test\n    void testGroupBy(){\n        Assertions.assertEquals(\"GROUP BY id\", new QueryWrapper<Entity>().groupBy(\"id\").getSqlSegment().trim());\n        Assertions.assertEquals(\"GROUP BY id,name\", new QueryWrapper<Entity>().groupBy(Arrays.asList(\"id\", \"name\")).getSqlSegment().trim());\n        Assertions.assertEquals(\"GROUP BY id,name\", new QueryWrapper<Entity>().groupBy(List.of(\"id\", \"name\")).getSqlSegment().trim());\n        Assertions.assertEquals(\"GROUP BY id,name\", new QueryWrapper<Entity>().groupBy(new ArrayList<>() {{\n            add(\"id\");\n            add(\"name\");\n        }}).getSqlSegment().trim());\n    }\n\n    private List<Object> getList() {\n        List<Object> list = new ArrayList<>();\n        for (int i = 0; i < 2; i++) {\n            list.add(i);\n        }\n        return list;\n    }\n\n    private Map<String, Object> getMap() {\n        Map<String, Object> map = new HashMap<>();\n        for (int i = 0; i < 2; i++) {\n            map.put(\"column\" + i, i);\n        }\n        map.put(\"nullColumn\", null);\n        return map;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/UpdateWrapperIncrDecrTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.Update;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.test.User;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\nclass UpdateWrapperIncrDecrTest extends BaseWrapperTest {\n\n    @BeforeAll\n    static void initUser() {\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), User.class);\n        Assertions.assertEquals(\"sys_user\", tableInfo.getTableName());\n    }\n\n    @Test\n    void testIncrByAndDecrBy() {\n        assertEquals(new UpdateWrapper<User>()\n                .setIncrBy(\"role_id\", 1).setDecrBy(\"username\", 1),\n            \"role_id=role_id + 1,username=username - 1\");\n\n        assertEquals(new LambdaUpdateWrapper<User>()\n                .setIncrBy(User::getRoleId, 1).setDecrBy(User::getName, 1),\n            \"role_id=role_id + 1,username=username - 1\");\n    }\n\n    @Test\n    void testIncrByAndDecrByBigDecimal() {\n        assertEquals(new LambdaUpdateWrapper<User>()\n                .setIncrBy(User::getRoleId, new BigDecimal(\"1\"))\n                .setIncrBy(User::getRoleId, new BigDecimal(1))\n                .setIncrBy(User::getRoleId, new BigDecimal(1.0000))\n                .setIncrBy(User::getRoleId, new BigDecimal(\"1.0000\"))\n                .setIncrBy(User::getRoleId, new BigDecimal(\"0.01\"))\n                .setIncrBy(User::getRoleId, new BigDecimal(\"2340\")),\n            \"role_id=role_id + 1,role_id=role_id + 1,role_id=role_id + 1,\" +\n                \"role_id=role_id + 1.0000,role_id=role_id + 0.01,role_id=role_id + 2340\");\n\n        assertEquals(new LambdaUpdateWrapper<User>()\n                .setDecrBy(User::getRoleId, new BigDecimal(\"1\"))\n                .setDecrBy(User::getRoleId, new BigDecimal(1))\n                .setDecrBy(User::getRoleId, new BigDecimal(1.0000))\n                .setDecrBy(User::getRoleId, new BigDecimal(\"1.0000\"))\n                .setDecrBy(User::getRoleId, new BigDecimal(\"0.01\"))\n                .setDecrBy(User::getRoleId, new BigDecimal(\"2340\")),\n            \"role_id=role_id - 1,role_id=role_id - 1,role_id=role_id - 1,\" +\n                \"role_id=role_id - 1.0000,role_id=role_id - 0.01,role_id=role_id - 2340\");\n    }\n\n\n    private void assertEquals(Update<?, ?> update, String sql) {\n        Assertions.assertEquals(sql, update.getSqlSet());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/conditions/UpdateWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.core.conditions;\n\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.test.User;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\nclass UpdateWrapperTest extends BaseWrapperTest {\n\n    @Test\n    void test1() {\n        UpdateWrapper<User> wrapper = new UpdateWrapper<User>().checkSqlInjection().eq(\"hi=1 or a\", 123)\n            .set(\"name\", \"a\", \"typeHandler=org.apache.ibatis.type.StringTypeHandler\")\n            .set(\"name\", \"a\", \"typeHandler=org.apache.ibatis.type.StringTypeHandler,jdbcType=VARCHAR\")\n            .set(\"name\", \"a\", \"typeHandler=org.apache.ibatis.type.StringTypeHandler,jdbcType=VARCHAR,numericScale=2\");\n        logSqlSet(\"ss\", wrapper,\n            \"name=#{ew.paramNameValuePairs.MPGENVAL1,typeHandler=org.apache.ibatis.type.StringTypeHandler},\" +\n                \"name=#{ew.paramNameValuePairs.MPGENVAL2,typeHandler=org.apache.ibatis.type.StringTypeHandler,jdbcType=VARCHAR},\" +\n                \"name=#{ew.paramNameValuePairs.MPGENVAL3,typeHandler=org.apache.ibatis.type.StringTypeHandler,jdbcType=VARCHAR,numericScale=2}\");\n        logParams(wrapper);\n        Assertions.assertThrows(MybatisPlusException.class, wrapper::getSqlSegment);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/handlers/MetaObjectHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.core.handlers;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.ToString;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-11\n */\nclass MetaObjectHandlerTest {\n\n    private static final MybatisConfiguration configuration = new MybatisConfiguration();\n    private static final MapperBuilderAssistant assistant = new MapperBuilderAssistant(configuration, \"\");\n\n    @BeforeAll\n    static void initXt() {\n        TableInfoHelper.initTableInfo(assistant, Xt.class);\n    }\n\n    @Test\n    void strictInsertFill1() {\n        final MetaObjectHandler handler = new MetaObjectHandler() {\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                strictInsertFill(metaObject, \"name\", String.class, \"2222\");\n                strictInsertFill(metaObject, \"age\", Integer.class, 222);\n                strictInsertFill(metaObject, \"birthday\", LocalDate.class, LocalDate.now());\n                strictInsertFill(metaObject, \"dateTime\", LocalDateTime.class, LocalDateTime.now());\n                // set 子类\n                strictInsertFill(metaObject, \"ojbk\", Ojbk.class, new OjbkXx());\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n            }\n        };\n\n        Xt xt = new Xt();\n        MetaObject metaObject = configuration.newMetaObject(xt);\n        handler.insertFill(metaObject);\n        check(xt);\n    }\n\n    @Test\n    void strictInsertFill2() {\n        final MetaObjectHandler handler = new MetaObjectHandler() {\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                strictInsertFill(metaObject, \"name\", () -> \"2222\", String.class);\n                strictInsertFill(metaObject, \"age\", () -> 222, Integer.class);\n                strictInsertFill(metaObject, \"birthday\", LocalDate::now, LocalDate.class);\n                strictInsertFill(metaObject, \"dateTime\", LocalDateTime::now, LocalDateTime.class);\n                // set 子类\n                strictInsertFill(metaObject, \"ojbk\", OjbkXx::new, Ojbk.class);\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n            }\n        };\n\n        Xt xt = new Xt();\n        MetaObject metaObject = configuration.newMetaObject(xt);\n        handler.insertFill(metaObject);\n        check(xt);\n    }\n\n    @Test\n    void strictUpdateFill1() {\n        final MetaObjectHandler handler = new MetaObjectHandler() {\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                strictUpdateFill(metaObject, \"name\", String.class, \"2222\");\n                strictUpdateFill(metaObject, \"age\", Integer.class, 222);\n                strictUpdateFill(metaObject, \"birthday\", LocalDate.class, LocalDate.now());\n                strictUpdateFill(metaObject, \"dateTime\", LocalDateTime.class, LocalDateTime.now());\n                // set 子类\n                strictUpdateFill(metaObject, \"ojbk\", Ojbk.class, new OjbkXx());\n            }\n        };\n\n        Xt xt = new Xt();\n        MetaObject metaObject = configuration.newMetaObject(xt);\n        handler.updateFill(metaObject);\n        check(xt);\n    }\n\n    @Test\n    void strictUpdateFill2() {\n        final MetaObjectHandler handler = new MetaObjectHandler() {\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                strictUpdateFill(metaObject, \"name\", () -> \"2222\", String.class);\n                strictUpdateFill(metaObject, \"age\", () -> 222, Integer.class);\n                strictUpdateFill(metaObject, \"birthday\", LocalDate::now, LocalDate.class);\n                strictUpdateFill(metaObject, \"dateTime\", LocalDateTime::now, LocalDateTime.class);\n                // set 子类\n                strictUpdateFill(metaObject, \"ojbk\", OjbkXx::new, Ojbk.class);\n            }\n        };\n\n        Xt xt = new Xt();\n        MetaObject metaObject = configuration.newMetaObject(xt);\n        handler.updateFill(metaObject);\n        check(xt);\n    }\n\n    void check(Xt xt) {\n        System.out.println(xt);\n        assertThat(xt.getName()).isNotNull();\n        assertThat(xt.getAge()).isNotNull();\n        assertThat(xt.getBirthday()).isNotNull();\n        assertThat(xt.getDateTime()).isNotNull();\n        assertThat(xt.getOjbk()).isNotNull();\n    }\n\n    @Data\n    static class Xt {\n        private Long id;\n\n        @TableField(fill = FieldFill.INSERT_UPDATE)\n        private String name;\n        @TableField(fill = FieldFill.INSERT_UPDATE)\n        private Integer age;\n        @TableField(fill = FieldFill.INSERT_UPDATE)\n        private LocalDate birthday;\n        @TableField(fill = FieldFill.INSERT_UPDATE)\n        private LocalDateTime dateTime;\n        @TableField(fill = FieldFill.INSERT_UPDATE)\n        private Ojbk ojbk;\n    }\n\n    @Data\n    static class Ojbk {\n        private String xx = \"木大木大木大木大,欧拉欧拉欧拉欧拉\";\n    }\n\n    @Data\n    @ToString(callSuper = true)\n    @EqualsAndHashCode(callSuper = true)\n    static class OjbkXx extends Ojbk {\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/incrementer/ImadcnIdentifierGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.core.incrementer;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author miemie\n * @since 2020-08-11\n */\n@Disabled(\"不需要参与全局test\")\nclass ImadcnIdentifierGeneratorTest {\n\n    private static ImadcnIdentifierGenerator generator;\n\n    @BeforeAll\n    static void init() {\n        generator = new ImadcnIdentifierGenerator(\"localhost:2181\");\n    }\n\n    @AfterAll\n    static void close() throws Exception {\n        generator.close();\n    }\n\n    @Test\n    void nextId() {\n        for (int i = 0; i < 10; i++) {\n            System.out.println(generator.nextId(null));\n            System.out.println(generator.nextUUID(null));\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/metadata/TableInfoTest.java",
    "content": "package com.baomidou.mybatisplus.core.metadata;\n\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author nieiqurong\n */\npublic class TableInfoTest {\n\n    static class Demo {\n\n        String name;\n\n    }\n\n    @Test\n    void testCreate() {\n        TableInfo tableInfo;\n        Configuration configuration = new Configuration();\n        configuration.setMapUnderscoreToCamelCase(true);\n        tableInfo = new TableInfo(configuration, Demo.class);\n        Demo demo = tableInfo.newInstance();\n        tableInfo.setPropertyValue(demo, \"name\", \"test\");\n        assertThat(tableInfo.getPropertyValue(demo, \"name\")).isEqualTo(\"test\");\n        assertThat(tableInfo.isUnderCamel()).isTrue();\n        assertThat(tableInfo.getReflector()).isNotNull();\n        tableInfo = new TableInfo(configuration, Object.class);\n        assertThat(tableInfo.isUnderCamel()).isTrue();\n        assertThat(tableInfo.getReflector()).isNotNull();\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/plugins/InterceptorIgnoreHelperTest.java",
    "content": "package com.baomidou.mybatisplus.core.plugins;\n\nimport com.baomidou.mybatisplus.annotation.InterceptorIgnore;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Method;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-08-02\n */\nclass InterceptorIgnoreHelperTest {\n\n    @BeforeEach\n    void beforeEach() {\n        init(Xx.class);\n        init(Pp.class);\n        init(Gg.class);\n        init(ExtPp1.class);\n        init(ExtPp2.class);\n        init(ExtPp3.class);\n    }\n\n    @Test\n    void m1() {\n        checkTenantLine(Xx.class, \"gg\", true);\n        checkTenantLine(Xx.class, \"hh\", false);\n        checkTenantLine(Xx.class, \"mm\", true);\n\n        checkTenantLine(Pp.class, \"pp\", false);\n        checkTenantLine(Pp.class, \"dd\", true);\n        checkTenantLine(Pp.class, \"mj\", false);\n\n        checkTenantLine(Gg.class, \"uu\", true);\n\n        checkOthers(Xx.class, \"gg\", \"loli\", false);\n        checkOthers(Xx.class, \"gg\", \"mn\", false);\n        checkOthers(Xx.class, \"hh\", \"loli\", true);\n        checkOthers(Xx.class, \"hh\", \"mn\", true);\n\n        checkOthers(Pp.class, \"pp\", \"loli\", true);\n        checkOthers(Pp.class, \"dd\", \"loli\", false);\n\n        // 不存在的\n        checkOthers(Pp.class, \"mj\", \"xxxxx\", false);\n\n        checkTenantLine(ExtPp1.class, \"pp\", false);\n        checkTenantLine(ExtPp1.class, \"dd\", true);\n        checkTenantLine(ExtPp1.class, \"mj\", false);\n\n        checkTenantLine(ExtPp2.class, \"pp\", false);\n        checkTenantLine(ExtPp2.class, \"dd\", true);\n\n        // 缺省取当前类的\n        checkTenantLine(ExtPp2.class, \"mj\", false);\n\n        checkTenantLine(ExtPp3.class, \"mj\", true);\n\n    }\n\n    private void init(Class<?> clazz) {\n        IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(clazz);\n        for (Method method : clazz.getMethods()) {\n            InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, clazz.getName(), method);\n        }\n    }\n\n    private void checkTenantLine(Class<?> clazz, String method, boolean mustTrue) {\n        boolean result = InterceptorIgnoreHelper.willIgnoreTenantLine(clazz.getName().concat(\".\").concat(method));\n        if (mustTrue) {\n            assertThat(result).isTrue();\n        } else {\n            assertThat(result).isFalse();\n        }\n    }\n\n    private void checkOthers(Class<?> clazz, String method, String key, boolean mustTrue) {\n        boolean result = InterceptorIgnoreHelper.willIgnoreOthersByKey(clazz.getName().concat(\".\").concat(method), key);\n        if (mustTrue) {\n            assertThat(result).isTrue();\n        } else {\n            assertThat(result).isFalse();\n        }\n    }\n\n    @InterceptorIgnore(tenantLine = \"on\", others = {\"loli@1\", \"mn@1\"})\n    interface Xx {\n\n        @InterceptorIgnore(others = {\"loli@0\", \"mn@0\"})\n        void gg();\n\n        @InterceptorIgnore(tenantLine = \"off\")\n        void hh();\n\n        @InterceptorIgnore(illegalSql = \"off\")\n        void mm();\n\n        void ds();\n    }\n\n    interface Pp {\n\n        @InterceptorIgnore(tenantLine = \"0\", others = \"loli@1\")\n        void pp();\n\n        @InterceptorIgnore(tenantLine = \"1\")\n        void dd();\n\n        @InterceptorIgnore\n        void mj();\n    }\n\n    @InterceptorIgnore(tenantLine = \"1\")\n    interface Gg {\n\n        void uu();\n    }\n\n    interface ExtPp1 extends Pp {\n\n    }\n\n    @InterceptorIgnore(tenantLine = \"1\")\n    interface ExtPp2 extends Pp {\n\n        @InterceptorIgnore(tenantLine = \"false\")\n        void mj();\n    }\n\n    @InterceptorIgnore(tenantLine = \"true\")\n    interface ExtPp3 extends ExtPp2 {\n\n        void mj();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/MybatisUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.override.MybatisMapperProxy;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionManager;\nimport org.apache.ibatis.session.defaults.DefaultSqlSession;\nimport org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\nimport org.mybatis.spring.SqlSessionTemplate;\n\nimport java.util.HashMap;\n\n/**\n * @author nieqiurong\n */\npublic class MybatisUtilsTest {\n\n    interface MyMapper {\n\n    }\n\n    @Test\n    void testGetSqlSessionFactoryByDefaultSqlSession() {\n        var configuration = getMybatisConfiguration();\n        GlobalConfigUtils.getGlobalConfig(configuration).setSqlSessionFactory(Mockito.mock(SqlSessionFactory.class));\n        var sqlSession = new DefaultSqlSession(configuration, Mockito.mock(Executor.class));\n        var mybatisMapperProxy = new MybatisMapperProxy<>(sqlSession, MyMapper.class, new HashMap<>());\n        SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mybatisMapperProxy);\n        Assertions.assertNotNull(sqlSessionFactory);\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(sqlSession));\n    }\n\n    @Test\n    void testGetSqlSessionFactoryBySqlSessionManager() {\n        var sqlSession = SqlSessionManager.newInstance(Mockito.mock(SqlSessionFactory.class));\n        var mybatisMapperProxy = new MybatisMapperProxy<>(sqlSession, MyMapper.class, new HashMap<>());\n        SqlSessionFactory sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mybatisMapperProxy);\n        Assertions.assertNotNull(sqlSessionFactory);\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(sqlSession));\n    }\n\n    @Test\n    void testGetSqlSessionFactoryBySqlSessionTemplate() {\n        var sqlSession = new SqlSessionTemplate(getDefaultSqlSessionFactory());\n        var mybatisMapperProxy = new MybatisMapperProxy<>(sqlSession, MyMapper.class, new HashMap<>());\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(mybatisMapperProxy));\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(sqlSession));\n    }\n\n    static class MySqlSessionTemplate extends SqlSessionTemplate {\n\n        public MySqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {\n            super(sqlSessionFactory);\n        }\n    }\n\n    @Test\n    void testGetSqlSessionFactoryByExtendSqlSessionTemplate() {\n        var sqlSession = new MySqlSessionTemplate(getDefaultSqlSessionFactory());\n        var mybatisMapperProxy = new MybatisMapperProxy<>(sqlSession, MyMapper.class, new HashMap<>());\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(mybatisMapperProxy));\n        Assertions.assertNotNull(MybatisUtils.getSqlSessionFactory(sqlSession));\n    }\n\n    private SqlSessionFactory getDefaultSqlSessionFactory() {\n        return new DefaultSqlSessionFactory(getMybatisConfiguration());\n    }\n\n    private MybatisConfiguration getMybatisConfiguration() {\n        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(Mockito.mock(Environment.class));\n        mybatisConfiguration.addMapper(MyMapper.class);\n        return mybatisConfiguration;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/SequenceTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2022-10-17\n */\nclass SequenceTest {\n\n    @Test\n    void nextId() {\n        Sequence sequence = new Sequence(null);\n        long id = sequence.nextId();\n        LocalDateTime now = LocalDateTime.now();\n        System.out.println(sequence.nextId() + \"---\" + now);\n\n        long timestamp = Sequence.parseIdTimestamp(id);\n        Instant instant = Instant.ofEpochMilli(timestamp);\n        ZoneId zone = ZoneId.systemDefault();\n        LocalDateTime time = LocalDateTime.ofInstant(instant, zone);\n        System.out.println(timestamp + \"---\" + time);\n        assertThat(now).isAfter(time);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/reflect/TypeParameterResolverTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit.reflect;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertSame;\n\n/**\n * Create by hcl at 2023/9/25\n */\nclass TypeParameterResolverTest {\n\n    static class CA<E> {\n    }\n\n    static class CB<A, B> extends CA<B> {\n    }\n\n    static class MyEntity extends CB<String, Number> {\n    }\n\n    interface Mapper1<T> extends BaseMapper<T> {\n    }\n\n    interface Mapper2 extends Mapper<MyEntity> {\n    }\n\n    interface Mapper3 extends Mapper2 {\n    }\n\n    interface Mapper4<A, B, C> extends Mapper<B> {\n    }\n\n    interface Mapper5 extends Mapper4<Number, MyEntity, Boolean> {\n    }\n\n    @Test\n    void test() {\n        assertSame(null, TypeParameterResolver.resolveClassIndexedParameter(Mapper1.class, Mapper.class, 0));\n        assertSame(MyEntity.class, TypeParameterResolver.resolveClassIndexedParameter(Mapper2.class, Mapper.class, 0));\n        assertSame(MyEntity.class, TypeParameterResolver.resolveClassIndexedParameter(Mapper3.class, Mapper.class, 0));\n        assertSame(MyEntity.class, TypeParameterResolver.resolveClassIndexedParameter(Mapper5.class, Mapper.class, 0));\n\n        assertSame(Number.class, TypeParameterResolver.resolveClassIndexedParameter(MyEntity.class, CA.class, 0));\n        assertSame(Number.class, TypeParameterResolver.resolveClassIndexedParameter(MyEntity.class, CB.class, 1));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlInjectionUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * SQL 注入验证工具类测试\n *\n * @author hubin\n * @since 2021-08-15\n */\nclass SqlInjectionUtilsTest {\n\n    @Test\n    void sqlTest() {\n        assertSql(false, \"insert abc\");\n        assertSql(true, \"insert into user (id,name) value (1, 'qm')\");\n        assertSql(true, \"SELECT * FROM user\");\n        assertSql(true, \"delete from user\");\n        assertSql(true, \"drop TABLE user\");\n        assertSql(true, \";TRUNCATE from user\");\n        assertSql(false, \"update\");\n        assertSql(false, \"trigger\");\n        assertSql(false, \"convert(name using GBK)\");\n\n        // 无空格\n        assertSql(false, \"insert_into\");\n        assertSql(true, \"SELECT aa FROM user\");\n        // 无空格\n        assertSql(true, \"SELECT*FROM user\");\n        // 左空格\n        assertSql(true, \"SELECT *FROM user\");\n        // 右空格\n        assertSql(true, \"SELECT* FROM user\");\n        // 左tab\n        assertSql(true, \"SELECT                 *FROM user\");\n        // 右tab\n        assertSql(true, \"SELECT*        FROM user\");\n        assertSql(false, \"SELECT*FROMuser\");\n        // 该字符串里包含 setT or\n        assertSql(false, \"databaseType desc,orderNum desc)\");\n        // 双引号情况\n        assertSql(true, \"\\\\\\\" or 1=1 and \\\\\\\"123\\\\\\\"=\\\\\\\"123\\\\\\\"\");\n        //Wrapper的apply情况\n        assertSql(true, \"1 = 1) OR 1 = 1 --\");\n\n        // https://github.com/baomidou/mybatis-plus/pull/5438/files\n        assertSql(false, \"insert\");\n        assertSql(false, \"union\");\n        assertSql(false, \"or\");\n        assertSql(false, \"delete\");\n        assertSql(false, \"drop\");\n        assertSql(true, \"AND age not in (1,2,3)\");\n        assertSql(true, \"and age <> 1\");\n        assertSql(false, \"ORDER BY field(status,'SUCCESS','FAILED','CLOSED')\");\n        assertSql(true, \"ORDER BY id,'SUCCESS',''-- FAILED','CLOSED'\");\n        assertSql(true, \"or 1 = 1\");\n        assertSql(true, \"and 1 = 1\");\n        assertSql(true, \"hi = 1 or abc\");\n        assertSql(true, \"(hi = 1) and abc\");\n        assertSql(false, \"orAnd\");\n        assertSql(false, \"andOr\");\n        assertSql(false, \"andOr\");\n\n        // 函数验证\n        assertSql(true, \"if(2=2)\");\n        assertSql(false, \"if\");\n        assertSql(true, \"SUBSTR(name)\");\n        assertSql(true, \"substr(name)\");\n        assertSql(true, \"suBStr(name)\");\n        assertSql(false, \"suBStr\");\n        assertSql(true, \"SUBSTRING(name)\");\n        assertSql(true, \"CHAR(name)\");\n        assertSql(true, \"char(name)\");\n        assertSql(true, \"concat(name, '0')\");\n        assertSql(false, \"concat\");\n        assertSql(true, \"select(table_name) from info\");\n        assertSql(true, \",sleep(0.01)\");\n        assertSql(false, \",sleep\");\n        assertSql(true, \"DBMS_LOCK.sleep(0.01)\");\n        assertSql(true, \"1=1&&(if(substr((select(table_name) from information_schema.TABLES WHERE table_schema=database() limit 0,1),1,1)!='a',sleep(0.01),2))\");\n    }\n\n    private void assertSql(boolean injection, String sql) {\n        Assertions.assertEquals(injection, SqlInjectionUtils.check(sql));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlScriptUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.LocalDateTypeHandler;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2021-01-27\n */\nclass SqlScriptUtilsTest {\n\n    @Test\n    void convertParamMapping() {\n        assertThat(SqlScriptUtils.convertParamMapping(LocalDateTypeHandler.class, JdbcType.DATE,2))\n            .isEqualTo(\"typeHandler=org.apache.ibatis.type.LocalDateTypeHandler,jdbcType=DATE,numericScale=2\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/sql/SqlUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit.sql;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-11-19\n */\nclass SqlUtilsTest {\n\n    @Test\n    void m1() {\n        List<String> list = SqlUtils.findPlaceholder(\"select {@table},{@table-id,name},{@table:t},{@table-id,name:t},\" +\n            \"{@table:t:r},{@table-id,name:t:r}, from table\");\n        assertThat(list).contains(\"{@table}\", \"{@table-id,name}\", \"{@table:t}\", \"{@table-id,name:t}\", \"{@table:t:r}\", \"{@table-id,name:t:r}\");\n    }\n\n    @Test\n    void getNewSelectBody() {\n        String s = SqlUtils.getNewSelectBody(\"id,name\", \"d\", null, null);\n        assertThat(s).isEqualTo(\"d.id,d.name\");\n\n        s = SqlUtils.getNewSelectBody(\"`id`,`name`\", \"d\", null, null);\n        assertThat(s).isEqualTo(\"d.`id`,d.`name`\");\n\n        s = SqlUtils.getNewSelectBody(\"id,name\", \"d\", \"pp\", \"`\");\n        assertThat(s).isEqualTo(\"d.id AS `pp.id`,d.name AS `pp.name`\");\n\n        s = SqlUtils.getNewSelectBody(\"id AS t_id,name AS t_name\", \"d\", null, null);\n        assertThat(s).isEqualTo(\"d.id AS t_id,d.name AS t_name\");\n\n        s = SqlUtils.getNewSelectBody(\"`id` AS t_id,`name` AS t_name\", \"d\", null, null);\n        assertThat(s).isEqualTo(\"d.`id` AS t_id,d.`name` AS t_name\");\n\n        s = SqlUtils.getNewSelectBody(\"id AS `t_id`,name AS `t_name`\", \"d\", \"pp\", \"`\");\n        assertThat(s).isEqualTo(\"d.id AS `pp.t_id`,d.name AS `pp.t_name`\");\n\n        s = SqlUtils.getNewSelectBody(\"`id` AS `t_id`,`name` AS `t_name`\", \"d\", \"pp\", \"'\");\n        assertThat(s).isEqualTo(\"d.`id` AS 'pp.t_id',d.`name` AS 'pp.t_name'\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/core/toolkit/support/IdeaProxyLambdaMetaTest.java",
    "content": "package com.baomidou.mybatisplus.core.toolkit.support;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandleProxies;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\nimport java.lang.reflect.Proxy;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.function.IntSupplier;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * IDEA 代理 Lambda 表达式测试\n * <p>\n * Create by hcl at 2022/8/12\n */\nclass IdeaProxyLambdaMetaTest {\n\n    @Test\n    void test() throws NoSuchMethodException, IllegalAccessException {\n        MethodHandles.Lookup lookup = MethodHandles.lookup();\n        MethodHandle handle = lookup.findStatic(IdeaProxyLambdaMetaTest.class, \"s\", MethodType.methodType(int.class));\n\n        // 模拟 IDEA 的 lambda 生成\n        IntSupplier supplier = MethodHandleProxies.asInterfaceInstance(IntSupplier.class, handle);\n        IdeaProxyLambdaMeta meta = new IdeaProxyLambdaMeta((Proxy) supplier);\n        assertEquals(\"s\", meta.getImplMethodName());\n    }\n\n    private static int s() {\n        return ThreadLocalRandom.current().nextInt();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/EncryptTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.EncryptUtils;\nimport lombok.Data;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 加密测试\n */\nclass EncryptTest {\n\n    @Test\n    void md5Base64() {\n        Assertions.assertEquals(\"Jgmg8jeuq9EyB1ybYtj1fg==\",\n            EncryptUtils.md5Base64(\"犯我中华者虽远必诛\"));\n    }\n\n    @Test\n    void other() {\n        System.out.println(TableInfoHelper.checkRelated(true, \"order\", \"'order'\"));\n        System.out.println(TableInfoHelper.checkRelated(true, \"order\", \"order\"));\n        System.out.println(TableInfoHelper.checkRelated(true, \"orderFile\", \"'ORDER_FILE'\"));\n    }\n\n    @Test\n    void testTableInfoHelper() {\n        TableInfo info = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), Xx.class);\n        System.out.println(\"----------- AllInsertSqlColumn -----------\");\n        System.out.println(info.getAllInsertSqlColumnMaybeIf(\"et.\"));\n        System.out.println(\"----------- AllInsertSqlProperty -----------\");\n        System.out.println(info.getAllInsertSqlPropertyMaybeIf(\"et.\"));\n        System.out.println(\"----------- AllSqlSet -----------\");\n        System.out.println(info.getAllSqlSet(true, \"ew.entity.\"));\n        System.out.println(\"----------- AllSqlWhere -----------\");\n        System.out.println(info.getAllSqlWhere(false, true, true, \"ew.entity.\"));\n    }\n\n    @Data\n    private static class Xx {\n        @TableId(type = IdType.AUTO)\n        private Long id;\n        @TableField(fill = FieldFill.INSERT)\n        private String x1;\n        @TableField(fill = FieldFill.INSERT_UPDATE, whereStrategy = FieldStrategy.NOT_EMPTY)\n        private String x2;\n        @TableField(fill = FieldFill.UPDATE)\n        private String x3;\n        @TableField(whereStrategy = FieldStrategy.NOT_EMPTY)\n        private String x4;\n        @TableField(value = \"xx5\", updateStrategy = FieldStrategy.ALWAYS, update = \"%s+1\")\n        private String x5;\n        @TableLogic\n        private Integer deleted;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/MybatisConfigurationTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;\nimport com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;\nimport com.baomidou.mybatisplus.test.mapper.AMapper;\nimport com.baomidou.mybatisplus.test.mapper.BMapper;\nimport org.apache.ibatis.builder.StaticSqlSource;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.mapping.StatementType;\nimport org.apache.ibatis.session.*;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * @author nieqiurong 2019/2/23.\n */\nclass MybatisConfigurationTest {\n\n    @Test\n    void testXml() throws IOException {\n        Reader reader = Resources.getResourceAsReader(\"mybatis-config-empty.xml\");\n        SqlSessionFactory factory = new MybatisSqlSessionFactoryBuilder().build(reader);\n        Configuration configuration = factory.getConfiguration();\n        Assertions.assertTrue(configuration.isCacheEnabled());\n        //3.4.1以下是true\n        Assertions.assertFalse(configuration.isAggressiveLazyLoading());\n        Assertions.assertTrue(configuration.isMultipleResultSetsEnabled());\n        Assertions.assertTrue(configuration.isUseColumnLabel());\n        Assertions.assertFalse(configuration.isUseGeneratedKeys());\n        Assertions.assertEquals(AutoMappingBehavior.PARTIAL,configuration.getAutoMappingBehavior());\n        Assertions.assertEquals(AutoMappingUnknownColumnBehavior.NONE,configuration.getAutoMappingUnknownColumnBehavior());\n        Assertions.assertEquals(ExecutorType.SIMPLE,configuration.getDefaultExecutorType());\n        Assertions.assertNull(configuration.getDefaultStatementTimeout());\n        Assertions.assertNull(configuration.getDefaultFetchSize());\n        Assertions.assertFalse(configuration.isSafeRowBoundsEnabled());\n        Assertions.assertTrue(configuration.isSafeResultHandlerEnabled());\n        //这里原生的是false\n        Assertions.assertTrue(configuration.isMapUnderscoreToCamelCase());\n        Assertions.assertEquals(LocalCacheScope.SESSION,configuration.getLocalCacheScope());\n        Assertions.assertEquals(JdbcType.OTHER,configuration.getJdbcTypeForNull());\n        Assertions.assertEquals(Stream.of(\"equals\",\"clone\",\"hashCode\",\"toString\").collect(Collectors.toSet()), configuration.getLazyLoadTriggerMethods());\n        Assertions.assertEquals(MybatisXMLLanguageDriver.class,configuration.getDefaultScriptingLanguageInstance().getClass());\n        Assertions.assertFalse(configuration.isCallSettersOnNulls());\n        Assertions.assertFalse(configuration.isReturnInstanceForEmptyRow());\n        Assertions.assertNull(configuration.getLogPrefix());\n        Assertions.assertNull(configuration.getLogImpl());\n        Assertions.assertEquals(JavassistProxyFactory.class,configuration.getProxyFactory().getClass());\n        Assertions.assertNull(configuration.getVfsImpl());\n        Assertions.assertTrue(configuration.isUseActualParamName());\n        Assertions.assertNull(configuration.getConfigurationFactory());\n\n    }\n\n    @Test\n    void testBean() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        Assertions.assertTrue(configuration.isCacheEnabled());\n        //3.4.1以下是true\n        Assertions.assertFalse(configuration.isAggressiveLazyLoading());\n        Assertions.assertTrue(configuration.isMultipleResultSetsEnabled());\n        Assertions.assertTrue(configuration.isUseColumnLabel());\n        Assertions.assertFalse(configuration.isUseGeneratedKeys());\n        Assertions.assertEquals(AutoMappingBehavior.PARTIAL,configuration.getAutoMappingBehavior());\n        Assertions.assertEquals(AutoMappingUnknownColumnBehavior.NONE,configuration.getAutoMappingUnknownColumnBehavior());\n        Assertions.assertEquals(ExecutorType.SIMPLE,configuration.getDefaultExecutorType());\n        Assertions.assertNull(configuration.getDefaultStatementTimeout());\n        Assertions.assertNull(configuration.getDefaultFetchSize());\n        Assertions.assertFalse(configuration.isSafeRowBoundsEnabled());\n        Assertions.assertTrue(configuration.isSafeResultHandlerEnabled());\n        //这里原生的是false\n        Assertions.assertTrue(configuration.isMapUnderscoreToCamelCase());\n        Assertions.assertEquals(LocalCacheScope.SESSION,configuration.getLocalCacheScope());\n        Assertions.assertEquals(JdbcType.OTHER,configuration.getJdbcTypeForNull());\n        Assertions.assertEquals(Stream.of(\"equals\",\"clone\",\"hashCode\",\"toString\").collect(Collectors.toSet()), configuration.getLazyLoadTriggerMethods());\n        Assertions.assertEquals(MybatisXMLLanguageDriver.class,configuration.getDefaultScriptingLanguageInstance().getClass());\n        Assertions.assertFalse(configuration.isCallSettersOnNulls());\n        Assertions.assertFalse(configuration.isReturnInstanceForEmptyRow());\n        Assertions.assertNull(configuration.getLogPrefix());\n        Assertions.assertNull(configuration.getLogImpl());\n        Assertions.assertEquals(JavassistProxyFactory.class,configuration.getProxyFactory().getClass());\n        Assertions.assertNull(configuration.getVfsImpl());\n        Assertions.assertTrue(configuration.isUseActualParamName());\n        Assertions.assertNull(configuration.getConfigurationFactory());\n    }\n\n    @Test\n    void testUseGeneratedShortKey(){\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setUseGeneratedShortKey(true);\n        addMappedStatement(configuration, 200);\n        Assertions.assertEquals(400, configuration.getMappedStatements().size());\n\n        configuration = new MybatisConfiguration();\n        configuration.setUseGeneratedShortKey(false);\n        addMappedStatement(configuration, 200);\n        Assertions.assertEquals(200, configuration.getMappedStatements().size());\n    }\n\n    private void addMappedStatement(Configuration configuration, int size) {\n        for (int i = 0; i < size; i++) {\n            configuration.addMappedStatement(\n                new MappedStatement.Builder(configuration, \"com.baomidou.test.method\" + i,\n                    new StaticSqlSource(configuration, \"select * from test\"), SqlCommandType.SELECT)\n                    .statementType(StatementType.STATEMENT).resource(\"xxxxxx\").useCache(true).keyGenerator(NoKeyGenerator.INSTANCE)\n                    .build()\n            );\n        }\n    }\n\n    @Test\n    void testReload() {\n        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();\n        mybatisConfiguration.addMapper(BMapper.class);\n        mybatisConfiguration.addMapper(AMapper.class);\n        Assertions.assertEquals(39, mybatisConfiguration.getMappedStatementNames().size());\n        Assertions.assertEquals(4, mybatisConfiguration.getSqlFragments().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getResultMaps().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getCaches().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getMapperRegistry().getMappers().size());\n        mybatisConfiguration.addNewMapper(BMapper.class);\n        mybatisConfiguration.addNewMapper(AMapper.class);\n        Assertions.assertEquals(39, mybatisConfiguration.getMappedStatementNames().size());\n        Assertions.assertEquals(4, mybatisConfiguration.getSqlFragments().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getResultMaps().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getCaches().size());\n        Assertions.assertEquals(2, mybatisConfiguration.getMapperRegistry().getMappers().size());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/MybatisMapperAnnotationBuilderTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.MapKey;\nimport org.apache.ibatis.annotations.ResultType;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.reflection.TypeParameterResolver;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.*;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * @author miemie\n * @since 2019-03-22\n */\n@SuppressWarnings(\"all\")\nclass MybatisMapperAnnotationBuilderTest {\n\n    Class<?> type = Mapper.class;\n\n    @Test\n    void test() {\n        Method[] methods = type.getMethods();\n        for (Method method : methods) {\n            Class<?> returnType = getReturnType(method);\n            System.out.println(method.getName() + \"        ====================       \" + returnType.getSimpleName());\n        }\n    }\n\n    private Class<?> getReturnType(Method method) {\n        Class<?> returnType = method.getReturnType();\n        Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);\n        if (resolvedReturnType instanceof Class) {\n            returnType = (Class<?>) resolvedReturnType;\n            if (returnType.isArray()) {\n                returnType = returnType.getComponentType();\n            }\n            // gcode issue #508\n            if (void.class.equals(returnType)) {\n                ResultType rt = method.getAnnotation(ResultType.class);\n                if (rt != null) {\n                    returnType = rt.value();\n                }\n            }\n        } else if (resolvedReturnType instanceof ParameterizedType) {\n            ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;\n            Class<?> rawType = (Class<?>) parameterizedType.getRawType();\n            if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                if (actualTypeArguments != null && actualTypeArguments.length == 1) {\n                    Type returnTypeParameter = actualTypeArguments[0];\n                    if (returnTypeParameter instanceof Class<?>) {\n                        returnType = (Class<?>) returnTypeParameter;\n                    } else if (returnTypeParameter instanceof ParameterizedType) {\n                        // (gcode issue #443) actual type can be a also a parameterized type\n                        returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                    } else if (returnTypeParameter instanceof GenericArrayType) {\n                        Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();\n                        // (gcode issue #525) support List<byte[]>\n                        returnType = Array.newInstance(componentType, 0).getClass();\n                    }\n                }\n            } else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {\n                // (gcode issue 504) Do not look into Maps if there is not MapKey annotation\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                if (actualTypeArguments != null && actualTypeArguments.length == 2) {\n                    Type returnTypeParameter = actualTypeArguments[1];\n                    if (returnTypeParameter instanceof Class<?>) {\n                        returnType = (Class<?>) returnTypeParameter;\n                    } else if (returnTypeParameter instanceof ParameterizedType) {\n                        // (gcode issue 443) actual type can be a also a parameterized type\n                        returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                    }\n                }\n            } else if (Optional.class.equals(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                Type returnTypeParameter = actualTypeArguments[0];\n                if (returnTypeParameter instanceof Class<?>) {\n                    returnType = (Class<?>) returnTypeParameter;\n                }\n            }\n            // TODO 下面是支援 IPage 及其子类作为返回值的\n            else if (IPage.class.isAssignableFrom(rawType)) {\n                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                Type returnTypeParameter = actualTypeArguments[0];\n                if (returnTypeParameter instanceof Class<?>) {\n                    returnType = (Class<?>) returnTypeParameter;\n                } else if (returnTypeParameter instanceof ParameterizedType) {\n                    returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();\n                }\n            }\n            // TODO 上面是支援 IPage 及其子类作为返回值的\n        }\n\n        return returnType;\n    }\n\n    interface Mapper {\n\n        Xxx one();\n\n        IPage<Xxx> xxxPage();\n\n        IPage<Map<String, Object>> mapPage();\n\n        Map<String, Object> selectMap();\n    }\n\n    class Xxx {\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/MybatisParameterHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.MybatisParameterHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.builder.StaticSqlSource;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.session.Configuration;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\n\nclass MybatisParameterHandlerTest {\n\n    @Data\n    @NoArgsConstructor\n    @AllArgsConstructor\n    private static class Model {\n\n        @TableId(type = IdType.INPUT)\n        private Long id;\n\n        private String name;\n\n        @TableField(fill = FieldFill.INSERT)\n        private String insertOperator;\n\n        @TableField(fill = FieldFill.UPDATE)\n        private String updateOperator;\n\n        Model(String name) {\n            this.name = name;\n        }\n\n        Model(Long id, String name) {\n            this.id = id;\n            this.name = name;\n        }\n    }\n\n    @Test\n    void test() {\n        Configuration configuration = new MybatisConfiguration();\n        BoundSql boundSql = mock(BoundSql.class);\n        StaticSqlSource staticSqlSource = mock(StaticSqlSource.class);\n        GlobalConfigUtils.getGlobalConfig(configuration).setIdentifierGenerator(new DefaultIdentifierGenerator()).setMetaObjectHandler(new MetaObjectHandler() {\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                setFieldValByName(\"id\", 666L, metaObject);\n                setFieldValByName(\"insertOperator\", \"咩咩\", metaObject);\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                setFieldValByName(\"updateOperator\", \"逗号\", metaObject);\n            }\n        });\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Model.class);\n\n        Model model = new Model(\"坦克\");\n        //查询\n        MappedStatement mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.SELECT).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getId()).isNull();\n        assertThat(model.getInsertOperator()).isNull();\n        assertThat(model.getUpdateOperator()).isNull();\n\n\n        // 普通插入\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getInsertOperator()).isNotNull();\n        assertThat(model.getUpdateOperator()).isNull();\n\n\n        //map参数\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        model = new Model(\"坦克\");\n        Map<String, Object> params1 = new HashMap<>();\n        params1.put(Constants.ENTITY, model);\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getInsertOperator()).isNotNull();\n        assertThat(model.getUpdateOperator()).isNull();\n//        //map参数\n//        Model model3 = new Model(\"坦克\");\n//        Map<String, Object> params2 = new HashMap<>();\n//        params2.put(Constants.ENTITY, new HashMap<String, Object>() {{\n//            put(Constants.MP_OPTLOCK_ET_ORIGINAL, model3);\n//        }});\n//        MybatisDefaultParameterHandler.processParameter(mappedStatement, params2);\n//        Assertions.assertNotNull(model3.getId());\n//        Assertions.assertNotNull(model3.getInsertOperator());\n//        Assertions.assertNull(model3.getUpdateOperator());\n\n        params1 = new HashMap<>();\n        Model model1 = new Model();\n        Model model2 = new Model();\n        params1.put(\"arrays\", new Model[]{model1, model2});\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n        model1 = new Model();\n        model2 = new Model();\n        params1 = new HashMap<>();\n        params1.put(Constants.ARRAY, new Model[]{model1, model2});\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n        model1 = new Model();\n        model2 = new Model();\n        params1 = new HashMap<>();\n        params1.put(Constants.ARRAY, new Object[]{model1, model2});\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n\n        model1 = new Model();\n        model2 = new Model();\n        params1 = new HashMap<>();\n        params1.put(Constants.LIST, Arrays.asList(model1, model2));\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n\n        model1 = new Model();\n        model2 = new Model();\n        params1 = new HashMap<>();\n        params1.put(Constants.COLL, Arrays.asList(model1, model2));\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n        model1 = new Model();\n        model2 = new Model();\n        params1 = new HashMap<>();\n        params1.put(\"collection\", Arrays.asList(model1, model2));\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        assertThat(model1.getId()).isNotNull();\n        assertThat(model2.getId()).isNotNull();\n        assertThat(model1.getInsertOperator()).isNotNull();\n        assertThat(model1.getUpdateOperator()).isNull();\n\n\n        params1 = new HashMap<>();\n        params1.put(Constants.ARRAY, new Object());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(Constants.ARRAY, new HashMap<>());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(Constants.ARRAY, new Object[]{});\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n\n        params1.put(Constants.ARRAY, null);\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n\n        params1 = new HashMap<>();\n        params1.put(Constants.COLL, new Object());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1 = new HashMap<>();\n        params1.put(Constants.COLL, new HashMap<>());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(Constants.COLL, null);\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(Constants.COLL, new ArrayList<>());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1 = new HashMap<>();\n        params1.put(\"collection\", new Object());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1 = new HashMap<>();\n        params1.put(\"collection\", new Object());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1 = new HashMap<>();\n        params1.put(\"collection\", new HashMap<>());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(\"collection\", null);\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n\n        params1.put(\"collection\", new ArrayList<>());\n        new MybatisParameterHandler(mappedStatement, params1, boundSql);\n        //普通更新\n        model = new Model(1L, \"坦克\");\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.UPDATE).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getUpdateOperator()).isNotNull();\n\n\n        //批量插入\n        List<Model> list = Arrays.asList(new Model(\"坦克一号\"), new Model(\"坦克二号\"));\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, list, boundSql);\n        list.forEach(m -> {\n            assertThat(m.getId()).isNotNull();\n            assertThat(m.getInsertOperator()).isNotNull();\n            assertThat(m.getUpdateOperator()).isNull();\n        });\n\n\n        //批量更新\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.UPDATE).build();\n        new MybatisParameterHandler(mappedStatement, list, boundSql);\n        list.forEach(m -> {\n            assertThat(m.getId()).isNotNull();\n            assertThat(m.getInsertOperator()).isNotNull();\n            assertThat(m.getUpdateOperator()).isNotNull();\n        });\n\n        Model[] arrays = new Model[]{new Model(\"坦克一号\"), new Model(\"坦克二号\")};\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, arrays, boundSql);\n        Arrays.stream(arrays).forEach(m -> {\n            assertThat(m.getId()).isNotNull();\n            assertThat(m.getInsertOperator()).isNotNull();\n            assertThat(m.getUpdateOperator()).isNull();\n        });\n\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.UPDATE).build();\n        new MybatisParameterHandler(mappedStatement, arrays, boundSql);\n        Arrays.stream(arrays).forEach(m -> {\n            assertThat(m.getId()).isNotNull();\n            assertThat(m.getInsertOperator()).isNotNull();\n            assertThat(m.getUpdateOperator()).isNotNull();\n        });\n\n        byte[] bytes1 = new byte[]{};\n        model = new Model();\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, Map.of(\"test\", bytes1, Constants.ENTITY, model), boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getInsertOperator()).isNotNull();\n        assertThat(model.getUpdateOperator()).isNull();\n\n        Byte[] bytes2 = new Byte[]{};\n        model = new Model();\n        mappedStatement = new MappedStatement.Builder(configuration, \"***\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, Map.of(\"test\", bytes2, Constants.ENTITY, model), boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getInsertOperator()).isNotNull();\n        assertThat(model.getUpdateOperator()).isNull();\n\n    }\n\n    @Test\n    void testIgnoreMapperStatement() {\n        MappedStatement mappedStatement;\n        Configuration configuration = new MybatisConfiguration();\n        BoundSql boundSql = mock(BoundSql.class);\n        StaticSqlSource staticSqlSource = mock(StaticSqlSource.class);\n        GlobalConfigUtils.getGlobalConfig(configuration).setIdentifierGenerator(DefaultIdentifierGenerator.getInstance()).setMetaObjectHandler(new MetaObjectHandler() {\n\n            @Override\n            public boolean openInsertFill(MappedStatement mappedStatement) {\n                //根据msId跳过填充\n                return !\"com.baomidou.demo.ignoreFillSave\".equals(mappedStatement.getId());\n            }\n\n            @Override\n            public boolean openUpdateFill(MappedStatement mappedStatement) {\n                //根据msId跳过填充\n                return !\"com.baomidou.demo.ignoreFillUpdate\".equals(mappedStatement.getId());\n            }\n\n            @Override\n            public void insertFill(MetaObject metaObject) {\n                setFieldValByName(\"id\", 666L, metaObject);\n                setFieldValByName(\"insertOperator\", \"秋秋\", metaObject);\n            }\n\n            @Override\n            public void updateFill(MetaObject metaObject) {\n                setFieldValByName(\"updateOperator\", \"秋秋\", metaObject);\n            }\n\n        });\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Model.class);\n        Model model;\n\n        model = new Model(\"测试新增忽略填充\");\n        mappedStatement = new MappedStatement.Builder(configuration, \"com.baomidou.demo.ignoreFillSave\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getId()).isNull();\n        assertThat(model.getInsertOperator()).isNull();\n\n        model = new Model(\"测试新增填充\");\n        mappedStatement = new MappedStatement.Builder(configuration, \"com.baomidou.demo.save\", staticSqlSource, SqlCommandType.INSERT).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getId()).isNotNull();\n        assertThat(model.getInsertOperator()).isNotNull();\n        assertThat(model.getInsertOperator()).isEqualTo(\"秋秋\");\n\n        model = new Model(\"测试更新忽略填充\");\n        mappedStatement = new MappedStatement.Builder(configuration, \"com.baomidou.demo.ignoreFillUpdate\", staticSqlSource, SqlCommandType.UPDATE).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getUpdateOperator()).isNull();\n\n        model = new Model(\"测试更新填充\");\n        mappedStatement = new MappedStatement.Builder(configuration, \"com.baomidou.demo.update\", staticSqlSource, SqlCommandType.UPDATE).build();\n        new MybatisParameterHandler(mappedStatement, model, boundSql);\n        assertThat(model.getUpdateOperator()).isEqualTo(\"秋秋\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/Role.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\n\n@Data\npublic class Role {\n\n    private Integer id;\n\n    @TableField(\"roleName\")\n    private String name;\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/User.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.Data;\n\n@TableName(\"sys_user\")\n@Data\npublic class User {\n\n    private Integer id;\n\n    @TableField(\"username\")\n    private String name;\n\n    private Integer roleId;\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/entity/AEntity.java",
    "content": "package com.baomidou.mybatisplus.test.entity;\n\nimport lombok.Data;\n\n@Data\npublic class AEntity {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/entity/BEntity.java",
    "content": "package com.baomidou.mybatisplus.test.entity;\n\nimport lombok.Data;\n\n@Data\npublic class BEntity {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/mapper/AMapper.java",
    "content": "package com.baomidou.mybatisplus.test.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.entity.AEntity;\n\npublic interface AMapper extends BaseMapper<AEntity> {\n\n\n    AEntity test();\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/mapper/BMapper.java",
    "content": "package com.baomidou.mybatisplus.test.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.entity.BEntity;\n\npublic interface BMapper extends BaseMapper<BEntity> {\n\n    BEntity test();\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/metadata/TableInfoHelperTest.java",
    "content": "package com.baomidou.mybatisplus.test.metadata;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.annotation.*;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nclass TableInfoHelperTest {\n\n    @Data\n    private static class BaseModel {\n\n        private Long id;\n\n        private String test;\n    }\n\n\n    @Data\n    private static class ModelOne {\n\n        @TableId\n        private Long id;\n\n        private String name;\n    }\n\n    @Data\n    private static class ModelTwo {\n\n        private Long id;\n\n        private String name;\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    @TableName(excludeProperty = \"test\")\n    private static class ModelThree extends BaseModel {\n\n        private String sex;\n\n        private String name;\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    @TableName(excludeProperty = {\"test\", \"id\"})\n    private static class ModelFour extends BaseModel {\n\n        private String sex;\n\n        private String name;\n    }\n\n    @Test\n    void testExcludeProperty() {\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelThree.class);\n        assertThat(tableInfo.havePK()).isTrue();\n        assertThat(tableInfo.getKeyProperty()).isEqualTo(\"id\");\n        assertThat(tableInfo.getFieldList().size()).isEqualTo(2);\n        assertThat(tableInfo.getFieldList()).noneMatch(i -> i.getProperty().equals(\"test\"));\n\n        tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelFour.class);\n        assertThat(tableInfo.getFieldList().size()).isEqualTo(2);\n        assertThat(tableInfo.getFieldList()).noneMatch(i -> i.getProperty().equals(\"test\"));\n\n        tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelFour2.class);\n        assertThat(tableInfo.getFieldList().size()).isEqualTo(1);\n        assertThat(tableInfo.getFieldList()).anyMatch(i -> i.getProperty().equals(\"name\"));\n        assertThat(tableInfo.havePK()).isTrue();\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableField(exist = false)\n    @interface NotExistsField {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableId\n    @interface MyId {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n    @TableName(schema = \"test\")\n    @interface MyTable {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableLogic(value = \"false\", delval = \"true\")\n    @interface MyTableLogic {\n\n    }\n\n\n    @Data\n    @MyTable\n    private static class CustomAnnotation {\n\n        @MyId\n        private Long id;\n\n        private String name;\n\n        @NotExistsField\n        private String test;\n\n        @MyTableLogic\n        private Boolean del;\n    }\n\n    @Test\n    void testCustomAnnotation() {\n        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(mybatisConfiguration, \"\"), CustomAnnotation.class);\n        List<Field> fieldList = TableInfoHelper.getAllFields(CustomAnnotation.class);\n        Assertions.assertThat(fieldList.size()).isEqualTo(3);\n        Assertions.assertThat(TableInfoHelper.isExistTableId(CustomAnnotation.class, Arrays.asList(CustomAnnotation.class.getDeclaredFields()))).isTrue();\n        Assertions.assertThat(TableInfoHelper.isExistTableLogic(CustomAnnotation.class, Arrays.asList(CustomAnnotation.class.getDeclaredFields()))).isTrue();\n        TableFieldInfo logicDeleteFieldInfo = TableInfoHelper.getTableInfo(CustomAnnotation.class).getLogicDeleteFieldInfo();\n        Assertions.assertThat(logicDeleteFieldInfo).isNotNull();\n        Assertions.assertThat(logicDeleteFieldInfo.getLogicDeleteValue()).isNotNull().isEqualTo(\"true\");\n        Assertions.assertThat(logicDeleteFieldInfo.getLogicNotDeleteValue()).isNotNull().isEqualTo(\"false\");\n        Assertions.assertThat(tableInfo.getTableName()).isEqualTo(\"test.custom_annotation\");\n    }\n\n\n    @Test\n    void testIsExistTableId() {\n        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(mybatisConfiguration, \"\"), ModelOne.class);\n        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(mybatisConfiguration, \"\"), ModelTwo.class);\n        Assertions.assertThat(TableInfoHelper.isExistTableId(ModelOne.class, Arrays.asList(ModelOne.class.getDeclaredFields()))).isTrue();\n        assertThat(TableInfoHelper.isExistTableId(ModelTwo.class, Arrays.asList(ModelTwo.class.getDeclaredFields()))).isFalse();\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    @TableName(properties = {\"id\", \"name\"}, excludeProperty = {\"sex\", \"test\"})\n    private static class ModelFour2 extends BaseModel {\n\n        private String sex;\n\n        private String name;\n    }\n\n    @Test\n    void testMoreTableId() {\n        Exception ex = null;\n        try {\n            TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelFive.class);\n        } catch (Exception e) {\n            ex = e;\n        }\n        assertThat(ex).isNotNull();\n        assertThat(ex).isInstanceOf(MybatisPlusException.class);\n        System.out.println(ex.getMessage());\n    }\n\n    @Test\n    void testPriorityTableId() {\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelSex.class);\n        assertThat(tableInfo.havePK()).isTrue();\n        assertThat(tableInfo.getKeyProperty()).isEqualTo(\"realId\");\n    }\n\n    @Test\n    void testVersion() {\n        Exception ex = null;\n        try {\n            TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), Versions.class);\n        } catch (Exception e) {\n            ex = e;\n        }\n        assertThat(ex).isNotNull();\n        assertThat(ex).isInstanceOf(MybatisPlusException.class);\n        System.out.println(ex.getMessage());\n    }\n\n    @Test\n    void testLogic() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        GlobalConfig config = GlobalConfigUtils.defaults();\n        config.getDbConfig().setLogicDeleteField(\"logic\");\n        GlobalConfigUtils.setGlobalConfig(configuration, config);\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Logic.class);\n        assertThat(tableInfo.isWithLogicDelete()).isTrue();\n        List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n        List<TableFieldInfo> logic = fieldList.stream().filter(TableFieldInfo::isLogicDelete).toList();\n        assertThat(logic.size()).isEqualTo(1);\n        assertThat(logic.getFirst().getProperty()).isEqualTo(\"deleted\");\n    }\n\n    @Test\n    void testColumnFormat() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        GlobalConfig config = GlobalConfigUtils.defaults();\n        GlobalConfig.DbConfig dbConfig = config.getDbConfig();\n        dbConfig.setColumnFormat(\"pxx_%s\");\n        dbConfig.setTableFormat(\"mp_%s\");\n        GlobalConfigUtils.setGlobalConfig(configuration, config);\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Logic.class);\n        List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n        fieldList.forEach(i -> {\n            assertThat(i.getColumn()).startsWith(\"pxx_\");\n        });\n        assertThat(tableInfo.getTableName()).startsWith(\"mp_\");\n    }\n\n    @Data\n    private static class ModelFive {\n\n        @TableId\n        private String id1;\n\n        @TableId\n        private String id2;\n    }\n\n    @Data\n    private static class ModelSex {\n\n        @TableId\n        private String realId;\n    }\n\n    @Data\n    private static class Logic {\n\n        private Integer logic;\n\n        @TableLogic\n        private Integer deleted;\n    }\n\n    @Data\n    private static class Versions {\n\n        @Version\n        private Integer version1;\n        @Version\n        private Integer version2;\n    }\n\n    @Test\n    void testTableNamePrefix() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        GlobalConfig config = GlobalConfigUtils.defaults();\n        config.getDbConfig().setTablePrefix(\"ttt_\");\n        GlobalConfigUtils.setGlobalConfig(configuration, config);\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Table.class);\n        assertThat(tableInfo.getTableName()).isEqualTo(\"xxx\");\n    }\n\n    @Data\n    @TableName(\"xxx\")\n    private static class Table {\n\n    }\n\n    @Test\n    void testTableNamePrefix2() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        GlobalConfig config = GlobalConfigUtils.defaults();\n        config.getDbConfig().setTablePrefix(\"ttt_\");\n        GlobalConfigUtils.setGlobalConfig(configuration, config);\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), Table2.class);\n        assertThat(tableInfo.getTableName()).isEqualTo(\"ttt_xxx\");\n    }\n\n    @Test\n    void getTableInfoInterface() {\n        TableInfoHelper.getTableInfo(Mapper.class);\n    }\n\n    @Data\n    @TableName(value = \"xxx\", keepGlobalPrefix = true)\n    private static class Table2 {\n\n    }\n\n\n    @Test\n    void testTableAutoResultMap() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, \"\"), AutoResultMapTable.class);\n        final ResultMap resultMap = tableInfo.getConfiguration().getResultMap(tableInfo.getResultMap());\n\n        assertThat(resultMap)\n            .isNotNull()\n            .extracting(ResultMap::getMappedColumns)\n            .matches((set) -> set.stream().noneMatch(StringUtils::isNotColumnName));\n    }\n\n    @Data\n    @TableName(value = \"xxx\", autoResultMap = true)\n    private static class AutoResultMapTable {\n\n        @TableId(\"`id`\")\n        private Long id;\n\n        @TableField(\"`name`\")\n        private Long name;\n    }\n\n    @Test\n    void testNewInstance() {\n        TableInfo tableInfo = TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), \"\"), ModelOne.class);\n        ModelOne entity = tableInfo.newInstance();\n        tableInfo.setPropertyValue(entity, tableInfo.getKeyColumn(), 1L);\n        assertThat(entity.id).isNotNull().isEqualTo(1L);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/override/MybatisMapperMethodTest.java",
    "content": "package com.baomidou.mybatisplus.test.override;\n\nimport org.apache.ibatis.annotations.MapKey;\nimport org.apache.ibatis.binding.BindingException;\nimport org.apache.ibatis.cursor.Cursor;\nimport org.apache.ibatis.reflection.TypeParameterResolver;\nimport org.apache.ibatis.reflection.factory.DefaultObjectFactory;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * @author miemie\n * @since 2019-03-22\n */\nclass MybatisMapperMethodTest {\n\n    @Test\n    void m1() {\n        Class<xxx> clazz = xxx.class;\n        MethodSignature signature = new MethodSignature(clazz, clazz.getMethods()[0]);\n        System.out.println(signature);\n    }\n\n    interface xxx {\n\n        Map<String, Object> map();\n    }\n\n    public static class MethodSignature {\n\n        private final boolean returnsMany;\n        private final boolean returnsMap;\n        private final boolean returnsVoid;\n        private final boolean returnsCursor;\n        private final boolean returnsOptional;\n        private final Class<?> returnType;\n        private final String mapKey;\n        private final Integer resultHandlerIndex;\n        private final Integer rowBoundsIndex;\n\n        public MethodSignature(Class<?> mapperInterface, Method method) {\n            Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);\n            if (resolvedReturnType instanceof Class<?>) {\n                this.returnType = (Class<?>) resolvedReturnType;\n            } else if (resolvedReturnType instanceof ParameterizedType) {\n                this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();\n            } else {\n                this.returnType = method.getReturnType();\n            }\n            this.returnsVoid = void.class.equals(this.returnType);\n            this.returnsMany = new DefaultObjectFactory().isCollection(this.returnType) || this.returnType.isArray();\n            this.returnsCursor = Cursor.class.equals(this.returnType);\n            this.returnsOptional = Optional.class.equals(this.returnType);\n            this.mapKey = getMapKey(method);\n            this.returnsMap = this.mapKey != null;\n            this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);\n            this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);\n        }\n\n        public boolean hasRowBounds() {\n            return rowBoundsIndex != null;\n        }\n\n        public RowBounds extractRowBounds(Object[] args) {\n            return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;\n        }\n\n        public boolean hasResultHandler() {\n            return resultHandlerIndex != null;\n        }\n\n        public ResultHandler extractResultHandler(Object[] args) {\n            return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;\n        }\n\n        public String getMapKey() {\n            return mapKey;\n        }\n\n        public Class<?> getReturnType() {\n            return returnType;\n        }\n\n        public boolean returnsMany() {\n            return returnsMany;\n        }\n\n        public boolean returnsMap() {\n            return returnsMap;\n        }\n\n        public boolean returnsVoid() {\n            return returnsVoid;\n        }\n\n        public boolean returnsCursor() {\n            return returnsCursor;\n        }\n\n        /**\n         * return whether return type is {@code java.util.Optional}\n         *\n         * @return return {@code true}, if return type is {@code java.util.Optional}\n         * @since 3.5.0\n         */\n        public boolean returnsOptional() {\n            return returnsOptional;\n        }\n\n        private Integer getUniqueParamIndex(Method method, Class<?> paramType) {\n            Integer index = null;\n            final Class<?>[] argTypes = method.getParameterTypes();\n            for (int i = 0; i < argTypes.length; i++) {\n                if (paramType.isAssignableFrom(argTypes[i])) {\n                    if (index == null) {\n                        index = i;\n                    } else {\n                        throw new BindingException(method.getName() + \" cannot have multiple \" + paramType.getSimpleName() + \" parameters\");\n                    }\n                }\n            }\n            return index;\n        }\n\n        private String getMapKey(Method method) {\n            String mapKey = null;\n            if (Map.class.isAssignableFrom(method.getReturnType())) {\n                final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);\n                if (mapKeyAnnotation != null) {\n                    mapKey = mapKeyAnnotation.value();\n                }\n            }\n            return mapKey;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\");) {\n            JerryParser jerryParser = Jerry.create(new LagartoDOMBuilder().enableXmlMode());\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency annotation = dependenciesMap.get(\"mybatis-plus-annotation\");\n            Assertions.assertEquals(\"compile\", annotation.getScope());\n            Assertions.assertFalse(annotation.isOptional());\n            Dependency mybatis = dependenciesMap.get(\"mybatis\");\n            Assertions.assertEquals(\"compile\", mybatis.getScope());\n            Assertions.assertFalse(mybatis.isOptional());\n            Dependency cglib = dependenciesMap.get(\"cglib\");\n            Assertions.assertEquals(\"compile\", cglib.getScope());\n            Assertions.assertTrue(cglib.isOptional());\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/pom/ReflectionKitTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Objects;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * 反射工具类测试\n */\npublic class ReflectionKitTest {\n\n    public static class MyEntity {\n    }\n\n    public interface Mapper1<T> extends BaseMapper<T> {\n    }\n\n    public interface Mapper2 extends Mapper<MyEntity> {\n    }\n\n    public interface Mapper3 extends Mapper2 {\n    }\n\n    @Test\n    void testSuperClassGenericType() {\n        // 多重继承测试\n        assertThat(Objects.equals(ReflectionKit.getSuperClassGenericType(Mapper2.class,\n            Mapper.class, 0), MyEntity.class));\n        assertThat(Objects.equals(ReflectionKit.getSuperClassGenericType(Mapper3.class,\n            Mapper.class, 0), MyEntity.class));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/AESTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.AES;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 测试 AES 算法\n *\n * @author hubin\n * @since 2020-05-23\n */\npublic class AESTest {\n\n    @Test\n    public void encryptDecrypt() {\n        String data = \"1@2#3qwe111\";\n        String randomKey = AES.generateRandomKey();\n        System.out.println(\"AES 密钥：\" + randomKey);\n        String result = AES.encrypt(data, randomKey);\n        System.out.println(\"AES 加密内容：\" + result);\n        Assertions.assertEquals(data, AES.decrypt(result, randomKey));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/AnnotationUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.core.toolkit.AnnotationUtils;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @author nieqiurong\n */\npublic class AnnotationUtilsTest {\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableField(exist = false)\n    @interface TableField2 {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableField\n    @interface TableField3 {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})\n    @TableField2\n    @interface TableField4 {\n\n    }\n\n    @Documented\n    @Retention(RetentionPolicy.RUNTIME)\n    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})\n    @TableName(schema = \"test\")\n    @interface MyTable {\n\n    }\n\n\n    @Data\n    @MyTable\n    public static class Demo {\n\n        private String id;\n\n        @TableField\n        private String name;\n\n        @TableField2\n        private String name1;\n\n        @TableField3\n        private String name2;\n\n        @TableField4\n        private String name3;\n\n        @TableField4\n        @TableField3\n        private String name4;\n\n        @TableField3\n        @TableField4\n        private String name5;\n\n        @TableField(fill = FieldFill.UPDATE)\n        @TableLogic(delval = \"true\", value = \"false\")\n        private Boolean deleted;\n\n    }\n\n    @Test\n    void test() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"id\"));\n        Assertions.assertNull(tableField);\n    }\n\n    @Test\n    void test1() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertTrue(tableField.exist());\n    }\n\n    @Test\n    void test2() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name1\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertFalse(tableField.exist());\n    }\n\n    @Test\n    void test3() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name2\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertTrue(tableField.exist());\n    }\n\n    @Test\n    void test4() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name3\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertFalse(tableField.exist());\n    }\n\n    @Test\n    void test5() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name4\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertFalse(tableField.exist());\n    }\n\n    @Test\n    void test6() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"name5\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertTrue(tableField.exist());\n    }\n\n    @Test\n    void test7() throws Exception {\n        TableField tableField = AnnotationUtils.findFirstAnnotation(TableField.class, Demo.class.getDeclaredField(\"deleted\"));\n        Assertions.assertNotNull(tableField);\n        Assertions.assertTrue(tableField.exist());\n        TableLogic tableLogic = AnnotationUtils.findFirstAnnotation(TableLogic.class, Demo.class.getDeclaredField(\"deleted\"));\n        Assertions.assertNotNull(tableLogic);\n    }\n\n    @Test\n    void test8() {\n        TableName tableName = AnnotationUtils.findFirstAnnotation(TableName.class, Demo.class);\n        Assertions.assertNotNull(tableName);\n        Assertions.assertEquals(\"test\", tableName.schema());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/BeanUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.BeanUtils;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport java.util.Map;\n\n/**\n * 测试 bean utils\n *\n * @author HCL\n * 2018/7/24 17:44\n */\nclass BeanUtilsTest {\n\n    /**\n     * 测试 beanToMap\n     */\n    @Test\n    @EnabledOnJre(JRE.JAVA_8)\n    void beanMapConvertTest() {\n        Map<String, Object> map = BeanUtils.beanToMap(new User() {{\n            setId(123);\n            setName(\"baomidou\");\n        }});\n        Assertions.assertEquals(2, map.size());\n        Assertions.assertEquals(123, map.get(\"id\"));\n        Assertions.assertEquals(\"baomidou\", map.get(\"name\"));\n        // 测试反向转换过程\n        User user = BeanUtils.mapToBean(map, User.class);\n        Assertions.assertEquals(123, user.getId());\n        Assertions.assertEquals(\"baomidou\", user.getName());\n\n    }\n\n    /**\n     * 自定义实体类用于测试\n     */\n    @Getter\n    @Setter\n    public static class User {\n        private String name;\n        private int id;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/ClassUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 测试\n *\n * @author HCL\n * 2018/7/26 9:53\n */\nclass ClassUtilsTest {\n\n    /**\n     * 测试实例化方法\n     */\n    @Test\n    void newInstanceTest() {\n        // 根据设计，只要能实例化，并且 对象不为 null ， 即认为执行成功\n        Demo demo = ClassUtils.newInstance(Demo.class);\n        Assertions.assertNotNull(demo);\n\n        Map<?,?> map = ClassUtils.newInstance(HashMap.class);\n        Assertions.assertNotNull(map);\n    }\n\n    /**\n     * 实例化的 demo class\n     */\n    private static class Demo {}\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/CollectionUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static java.util.Arrays.asList;\nimport static java.util.Collections.singletonList;\n\n/**\n * @author nieqiuqiu 2020/7/2\n */\nclass CollectionUtilsTest {\n\n    @Test\n    @EnabledOnJre(JRE.JAVA_8)\n    void testCreateHashMap() throws ReflectiveOperationException {\n        Map<String, String> hashMap = newHashMapWithExpectedSize(4);\n        Assertions.assertEquals(8, getTableSize(hashMap));\n        Assertions.assertEquals(6, getThresholdValue(hashMap));\n\n        hashMap = newHashMapWithExpectedSize(11);\n        Assertions.assertEquals(16, getTableSize(hashMap));\n        Assertions.assertEquals(12, getThresholdValue(hashMap));\n\n        hashMap = newHashMapWithExpectedSize(16);\n        Assertions.assertEquals(32, getTableSize(hashMap));\n        Assertions.assertEquals(24, getThresholdValue(hashMap));\n\n        Map<String, String> map = newHashMap(4);\n        Assertions.assertEquals(4, getTableSize(map));\n        Assertions.assertEquals(3, getThresholdValue(map));\n\n        map = newHashMap(11);\n        Assertions.assertEquals(16, getTableSize(map));\n        Assertions.assertEquals(12, getThresholdValue(map));\n\n        map = newHashMap(16);\n        Assertions.assertEquals(16, getTableSize(map));\n        Assertions.assertEquals(12, getThresholdValue(map));\n\n        computeIfAbsent();\n    }\n\n    @Test\n    void testSplit() {\n        List<Integer> list = asList(1, 2, 3, 4, 5);\n        List<List<Integer>> lists = CollectionUtils.split(list, 2);\n        Assertions.assertEquals(asList(asList(1, 2), asList(3, 4), singletonList(5)), lists);\n    }\n\n    private Map<String, String> newHashMapWithExpectedSize(int size) {\n        Map<String, String> hashMap = CollectionUtils.newHashMapWithExpectedSize(size);\n        hashMap.put(\"1\", \"1\");\n        return hashMap;\n    }\n\n    private Map<String, String> newHashMap(int size) {\n        Map<String, String> map = new HashMap<>(size);\n        map.put(\"1\", \"1\");\n        return map;\n    }\n\n    private int getTableSize(Map<String, String> map) throws ReflectiveOperationException {\n        Field field = map.getClass().getDeclaredField(\"table\");\n        field.setAccessible(true);\n        Object[] o = (Object[]) field.get(map);\n        return o.length;\n    }\n\n    private int getThresholdValue(Map<String, String> map) throws ReflectiveOperationException {\n        Field field = map.getClass().getDeclaredField(\"threshold\");\n        field.setAccessible(true);\n        return (int) field.get(map);\n    }\n\n    private void computeIfAbsent() {\n\n        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();\n        CollectionUtils.computeIfAbsent(map, \"AaAa\", key -> map.computeIfAbsent(\"BBBB\", key2 -> \"noah\"));\n        System.out.println(\"i can return\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/LambdaUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.LambdaUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;\nimport lombok.Getter;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandleProxies;\nimport java.lang.invoke.MethodHandles;\nimport java.lang.invoke.MethodType;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\n\n/**\n * 测试 Lambda 解析类\n */\nclass LambdaUtilsTest {\n\n    /**\n     * 测试解析\n     */\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    void testExtract() throws IllegalAccessException, NoSuchMethodException {\n        SFunction<TestModel, Object> function = TestModel::getName;\n        test(function);\n        MethodHandles.Lookup lookup = MethodHandles.lookup();\n        MethodHandle getter = lookup.findVirtual(TestModel.class, \"getId\", MethodType.methodType(int.class));\n        assertNotNull(SerializedLambda.extract(function));\n        function = (SFunction<TestModel, Object>) MethodHandleProxies.asInterfaceInstance(SFunction.class, getter);\n        test(function);\n    }\n\n    private void test(SFunction<TestModel, Object> function) {\n        function.apply(new TestModel());\n        LambdaMeta meta = LambdaUtils.extract(function);\n        assertNotNull(meta);\n        assertSame(TestModel.class, meta.getInstantiatedClass());\n    }\n\n    /**\n     * 用于测试的 Model\n     */\n    @Getter\n    public static class TestModel extends Parent implements Named {\n        private String name;\n    }\n\n    @Getter\n    private static abstract class Parent {\n        private int id;\n    }\n\n    // 处理 ISSUE:https://gitee.com/baomidou/mybatis-plus/issues/I13Y8Y，由于 java 本身处理的问题，这里无法获取到实例\n    private abstract static class BaseHolder<T extends Named> {\n\n        LambdaMeta toLambda() {\n            return LambdaUtils.extract(T::getName);\n        }\n\n    }\n\n    private static class TestModelHolder extends BaseHolder<TestModel> {\n    }\n\n    private interface Named {\n        String getName();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/ParameterUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.ParameterUtils;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\n\nclass ParameterUtilsTest {\n\n    static class Page<T> implements IPage<T> {\n        private long current = 1;\n        private long total = 0;\n        private long size = 10;\n        private List<T> records = Collections.emptyList();\n        private final List<OrderItem> orders = new ArrayList<>();\n\n        @Override\n        public List<OrderItem> orders() {\n            return orders;\n        }\n\n        @Override\n        public List<T> getRecords() {\n            return records;\n        }\n\n        @Override\n        public Page<T> setRecords(List<T> records) {\n            this.records = records;\n            return this;\n        }\n\n        @Override\n        public long getTotal() {\n            return this.total;\n        }\n\n        @Override\n        public Page<T> setTotal(long total) {\n            this.total = total;\n            return this;\n        }\n\n        @Override\n        public long getSize() {\n            return this.size;\n        }\n\n        @Override\n        public Page<T> setSize(long size) {\n            this.size = size;\n            return this;\n        }\n\n        @Override\n        public long getCurrent() {\n            return this.current;\n        }\n\n        @Override\n        public Page<T> setCurrent(long current) {\n            this.current = current;\n            return this;\n        }\n    }\n\n    @Test\n    void testFindPage() {\n        Assertions.assertFalse(ParameterUtils.findPage(null).isPresent());\n        MapperMethod.ParamMap<Object> param = new MapperMethod.ParamMap<>();\n        param.put(Constants.ENTITY, null);\n        Assertions.assertFalse(ParameterUtils.findPage(param).isPresent());\n        param.put(\"page\", new Page<>());\n        Assertions.assertTrue(ParameterUtils.findPage(param).isPresent());\n        Assertions.assertTrue(ParameterUtils.findPage(new Page<>()).isPresent());\n        Assertions.assertFalse(ParameterUtils.findPage(new HashMap<>()).isPresent());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/PluginUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport org.apache.ibatis.builder.StaticSqlSource;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.SimpleExecutor;\nimport org.apache.ibatis.executor.statement.RoutingStatementHandler;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.plugin.Intercepts;\nimport org.apache.ibatis.plugin.Invocation;\nimport org.apache.ibatis.plugin.Signature;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.RowBounds;\nimport org.apache.ibatis.transaction.jdbc.JdbcTransaction;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Proxy;\nimport java.sql.Connection;\n\n/**\n * @author nieqiurong 2023/8/5 10:28\n */\npublic class PluginUtilsTest {\n\n    @Intercepts({\n        @Signature(type = StatementHandler.class, method = \"prepare\", args = {Connection.class, Integer.class})}\n    )\n    static class TestInterceptor implements Interceptor {\n\n        @Override\n        public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {\n            return invocation.proceed();\n        }\n\n    }\n\n    @Test\n    void testRealTargetByProxy() {\n        Configuration configuration = new Configuration();\n        Executor executor = new SimpleExecutor(configuration, new JdbcTransaction(Mockito.mock(Connection.class)));\n        MappedStatement mappedStatement = new MappedStatement.Builder(configuration, \"test\", new StaticSqlSource(configuration, \"-------------\"), SqlCommandType.SELECT).build();\n        RoutingStatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, new Object(), new RowBounds(), null, Mockito.mock(BoundSql.class));\n        Object plugin = new TestInterceptor().plugin(statementHandler);\n        Assertions.assertTrue(Proxy.isProxyClass(plugin.getClass()));\n        Assertions.assertEquals(statementHandler, PluginUtils.realTarget(plugin));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/ReflectionKitTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Field;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 反射工具类测试\n *\n * @author nieqiuqiu 2019/1/16.\n */\nclass ReflectionKitTest {\n\n    @Data\n    private static class A {\n\n        private transient String test;\n\n        @SuppressWarnings(\"unused\")\n        private static String testStatic;\n\n        private String name;\n\n        private Boolean testWrap;\n\n        private boolean testSimple;\n\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    private static class B extends A {\n\n        private Integer age;\n\n    }\n\n    @Data\n    @EqualsAndHashCode(callSuper = true)\n    private static class C extends B {\n\n        private String sex;\n\n    }\n\n    @Data\n    private static class EntityByLombok {\n\n        private Long id;\n\n        private String pId;\n\n        private String parentId;\n\n    }\n\n    private static class Entity {\n\n        private Long id;\n\n        private String pId;\n\n        private String parentId;\n\n\n        public Long getId() {\n            return id;\n        }\n\n        public void setId(Long id) {\n            this.id = id;\n        }\n\n        public String getpId() {\n            return pId;\n        }\n\n        public void setpId(String pId) {\n            this.pId = pId;\n        }\n\n        public String getParentId() {\n            return parentId;\n        }\n\n        public void setParentId(String parentId) {\n            this.parentId = parentId;\n        }\n    }\n\n    @Test\n    void testGetFieldList() {\n        List<Field> fieldList = ReflectionKit.getFieldList(C.class);\n        Assertions.assertEquals(5, fieldList.size());\n    }\n\n    @Test\n    void testGetFieldMap() throws NoSuchFieldException {\n        Map<String, Field> fieldMap = ReflectionKit.getFieldMap(C.class);\n        Assertions.assertEquals(5, fieldMap.size());\n        Assertions.assertEquals(fieldMap.get(\"sex\"), C.class.getDeclaredField(\"sex\"));\n        Assertions.assertEquals(fieldMap.get(\"age\"), B.class.getDeclaredField(\"age\"));\n        Assertions.assertEquals(fieldMap.get(\"name\"), A.class.getDeclaredField(\"name\"));\n    }\n\n    @Test\n    void testGetFieldValue() {\n        C c = new C();\n        c.setSex(\"女\");\n        c.setName(\"妹纸\");\n        c.setAge(18);\n        Assertions.assertEquals(c.getSex(), ReflectionKit.getFieldValue(c, \"sex\"));\n        Assertions.assertEquals(c.getAge(), ReflectionKit.getFieldValue(c, \"age\"));\n\n        EntityByLombok entityByLombok = new EntityByLombok();\n        entityByLombok.setPId(\"6666\");\n        entityByLombok.setParentId(\"123\");\n        Assertions.assertEquals(entityByLombok.getPId(), ReflectionKit.getFieldValue(entityByLombok, \"pId\"));\n        Assertions.assertEquals(entityByLombok.getParentId(), ReflectionKit.getFieldValue(entityByLombok, \"parentId\"));\n\n        Entity entity = new Entity();\n        entity.setpId(\"6666\");\n        entity.setParentId(\"123\");\n        Assertions.assertEquals(entity.getParentId(), ReflectionKit.getFieldValue(entity, \"parentId\"));\n        Assertions.assertEquals(entity.getpId(), ReflectionKit.getFieldValue(entity, \"pId\"));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/StringUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * @author HCL\n * Create at 2018/9/17\n */\n\nclass StringUtilsTest {\n\n    @Test\n    void camelToUnderlineTest() {\n        String s = \"userId\";\n        Assertions.assertEquals(\"user_id\", StringUtils.camelToUnderline(s));\n        Assertions.assertEquals(\"a\", StringUtils.camelToUnderline(\"A\"));\n    }\n\n    @Test\n    void isCapitalModeTest() {\n        Assertions.assertFalse(StringUtils.isCapitalMode(\"test\"));\n        Assertions.assertFalse(StringUtils.isCapitalMode(\"Test\"));\n        Assertions.assertFalse(StringUtils.isCapitalMode(\"teSt\"));\n        assertTrue(StringUtils.isCapitalMode(\"TEST\"));\n    }\n\n    /**\n     * 测试当前字符串能否是合法的列名\n     */\n    @Test\n    void canBeAColumnName() {\n//        assertTrue(StringUtils.canBeColumnName(\"a$\"));\n    }\n\n    /**\n     * 取列名\n     */\n    @Test\n    void getTargetColumn() {\n        assertThat(StringUtils.getTargetColumn(\"order\")).isEqualTo(\"order\");\n        assertThat(StringUtils.getTargetColumn(\"`order`\")).isEqualTo(\"order\");\n        assertThat(StringUtils.getTargetColumn(\"'order'\")).isEqualTo(\"order\");\n    }\n\n    /**\n     * 测试equals方法\n     */\n    @Test\n    void equal(){\n        assertTrue(StringUtils.equals(null, null));\n        Assertions.assertFalse(StringUtils.equals(null, \"abc\"));\n        Assertions.assertFalse(StringUtils.equals(\"abc\", null));\n        assertTrue(StringUtils.equals(\"abc\", \"abc\"));\n        Assertions.assertFalse(StringUtils.equals(\"abc\", \"ABC\"));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/TableNameParserTest.java",
    "content": "package com.baomidou.mybatisplus.test.toolkit;\n\n\nimport com.baomidou.mybatisplus.core.toolkit.TableNameParser;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * TableNameParser Test\n *\n * @author Nadeem Mohammad\n * @since 2019-04-22\n */\npublic class TableNameParserTest {\n\n    private static final String SQL_SELECT_SUB_QUERY = \"SELECT /*+ materialize*/ strategy_id\"\n            + \"FROM\"\n            + \" ( SELECT  strat.cf_strategy_id \"\n            + \"   FROM strategy strt,\"\n            + \"        doc_sect_ver prodGrp\"\n            + \"  WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n            + \"           AND strat.module_type   IN ('sdfdsf','assdf')\"\n            + \")\";\n\n\n    private static final String SQL_SELECT_THREE_JOIN_WITH_ALIASE = \"select c.name, s.name, s.id, r.result\"\n            + \" from colleges c \"\n            + \" join students s\"\n            + \"   on c.id = s.college_id\"\n            + \" join results r\"\n            + \"   on s.id = r.student_id\"\n            + \"where c.id = 3\"\n            + \"  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\";\n\n    private static final String SQL_COMPLEX_ONE = \"INSERT INTO static_product\"\n            + \"  (\"\n            + \"   DISCOUNT_ID,\"\n            + \"    CATEGORY_ID,\"\n            + \"    PRODUCT_ID\"\n            + \"   )\"\n            + \"  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,\"\n            + \"     ALLNDC11.PRODUCT_ID,\"\n            + \"     ALLNDC11.NDC11\"\n            + \"  FROM ITEM ITEM\"\n            + \" INNER JOIN\"\n            + \"   (SELECT NODE.SOURCE_ID NDC11,\"\n            + \"    PR.PRODUCT_ID,\"\n            + \"     BD1.BUNDLE_DISCOUNT_ID\"\n            + \"    FROM DR_BUNDLE B,\"\n            + \"      DR_BUNDLE_DISCOUNT BD1,\"\n            + \"        DR_BD_PRODUCT PR,\"\n            + \"       map_edge_ver node\"\n            + \"     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n            + \"     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n            + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n            + \"    AND B.BUNDLE_STATUS         =3\"\n            + \"    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n            + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n            + \"     AND NODE.EDGE_TYPE          = 1\"\n            + \"      START WITH\"\n            + \"      (\"\n            + \"        NODE.DEST_ID              = PR.PRODUCT_ID\"\n            + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n            + \"      AND B.BUNDLE_STATUS         =3\"\n            + \"      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n            + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n            + \"      AND NODE.EDGE_TYPE          = 1\"\n            + \"      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n            + \"      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n            + \"     )\"\n            + \"       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID\"\n            + \"    AND PRIOR NODE.EDGE_TYPE           = 1\"\n            + \"    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n            + \"   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n            + \"    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)\"\n            + \"    ) ALLNDC11\"\n            + \"  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)\"\n            + \"  UNION\"\n            + \"   ( SELECT BD1.BUNDLE_DISCOUNT_ID,\"\n            + \"      PR.PRODUCT_ID,\"\n            + \"     ITEM.CAT_MAP_ID\"\n            + \"    FROM DR_BUNDLE B,\"\n            + \"      DR_BUNDLE_DISCOUNT BD1,\"\n            + \"     DR_BD_PRODUCT PR,\"\n            + \"    ITEM ITEM\"\n            + \"    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID\"\n            + \"   AND B.BUNDLE_STATUS         =3\"\n            + \"   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n            + \"    AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n            + \"   AND item.cat_map_id         =pr.product_id\"\n            + \"    )\";\n\n    private static final String SQL_MERGE_COMPLEX = \"MERGE INTO  cf_procedure proc USING\"\n            + \" (\"\n            + \" WITH NON_STRATEGY_DETAILS AS\"\n            + \"   (\"\n            + \"   SELECT /*+ materialize*/ cf_strategy_id\"\n            + \"    FROM\"\n            + \"     ( SELECT  strat.cf_strategy_id\"\n            + \"        FROM cf_strategy strat,\"\n            + \"             struct_doc_Sect_ver prodGrp\"\n            + \"        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n            + \"                 AND strat.src_mgr_id     = prodGrp.mgr_id\"\n            + \"                 AND strat.src_ver_num    = prodGrp.ver_num\"\n            + \"                 AND strat.module_type   IN ('COMPL','PRCMSTR')\"\n            + \"   )  ),\"\n            + \"   NON_STRATEGY_COMPS AS\"\n            + \"   (\"\n            + \"   SELECT /*+ materialize*/ cf_component_id\"\n            + \"   FROM\"\n            + \"   (\"\n            + \"     SELECT comp.cf_component_id AS cf_component_id\"\n            + \"     FROM   cf_component comp,\"\n            + \"            tier_basis_ver tb\"\n            + \"     WHERE  comp.bucket_src_id   = tb.tier_basis_id\"\n            + \"             AND comp.bucket_src_mgr_id  = tb.mgr_id\"\n            + \"             AND comp.bucket_src_ver_num = tb.ver_num\"\n            + \"             AND comp.module_type       IN ('COMPL','PRCMSTR')\"\n            + \"   )\"\n            + \"   ) ,\"\n            + \" NON_STRAT_PERIODS AS (\"\n            + \"   SELECT /*+ materialize*/ cf_period_id\"\n            + \"   FROM\"\n            + \"         cf_period per,\"\n            + \"         struct_doc_sect_ver prodGrp\"\n            + \"   WHERE  per.src_id            = prodGrp.struct_doc_sect_id\"\n            + \"         AND per.src_mgr_id     = prodGrp.mgr_id\"\n            + \"         AND per.src_ver_num    = prodGrp.ver_num\"\n            + \"         AND per.module_type    IN ('COMPL','PRCMSTR')\"\n            + \"         AND per.pmt_status NOT IN ('TERM','REV')\"\n\n            + \"    SELECT DISTINCT cf_procedure_id\"\n            + \"   FROM\"\n            + \"     (SELECT /*+ LEADING(comp,proc)*/\"\n            + \"           proc.cf_procedure_id AS cf_procedure_id\"\n            + \"     FROM  non_strategy_comps comp,\"\n            + \"           cf_procedure proc\"\n            + \"     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'\"\n            + \"           AND comp.cf_component_id    = proc.cf_component_id\"\n            + \"    UNION ALL\"\n            + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n            + \"           proc.cf_procedure_id AS cf_procedure_id\"\n            + \"     FROM  cf_procedure proc,\"\n            + \"           non_strategy_details strat\"\n            + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n            + \"           AND strat.cf_strategy_id = proc.cf_strategy_id\"\n            + \"     UNION ALL\"\n            + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n            + \"          proc.cf_procedure_id AS cf_procedure_id\"\n            + \"     FROM cf_procedure proc,\"\n            + \"          non_strat_periods periods\"\n            + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n            + \"           AND periods.CF_PERIOD_ID = proc.period_id\"\n            + \"     )\"\n            + \"      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id)\"\n            + \" WHEN MATCHED THEN\"\n            + \"   UPDATE SET proc.variable_name = 'TierResultSSName';\";\n\n    private static final String SQL_MERGE_COMPLEX_TWO = \" MERGE INTO cf_procedure_ver procVer USING\"\n            + \"   (SELECT cf_procedure_id\"\n            + \"    FROM cf_procedure proc\"\n            + \"    WHERE proc.variable_name                  = 'TierResultSSName'\"\n            + \"   ) proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id )\"\n            + \" WHEN MATCHED THEN\"\n            + \"   UPDATE SET procVer.variable_name = 'TierResultSSName'\"\n            + \"   WHERE procVer.variable_name <> 'TierResultSSName';\";\n\n    @Test\n    public void testSelectOneTable() {\n        String sql = \"SELECT name, age FROM table1 group by xyx\";\n        Assertions.assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n    @Test\n    public void testSelectTwoTables() {\n        String sql = \"SELECT name, age FROM table1,table2\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testSelectThreeTables() {\n        String sql = \"SELECT name, age FROM table1,table2,table3 group by xyx\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\", \"table3\"));\n    }\n\n    @Test\n    public void testSelectOneTableWithAliase() {\n        String sql = \"SELECT name, age FROM table1 t1 whatever group by xyx\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliase() {\n        String sql = \"SELECT name, age FROM table1 t1,table2 t2 whatever group by xyx\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliaseAndNoCondition() {\n        String sql = \"select xx from table1 a,table2 b\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testSelectThreeTablesWithAliase() {\n        String sql = \"SELECT name, age FROM table1 t1,table2 t2, table3 t3 whatever group by xyx\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\", \"table3\"));\n    }\n\n\n    @Test\n    public void testSelectWithSubQuery() {\n        assertThat(new TableNameParser(SQL_SELECT_SUB_QUERY).tables()).isEqualTo(asSet(\"strategy\", \"doc_sect_ver\"));\n    }\n\n    @Test\n    public void testSelectWithOneJoin() {\n        String sql = \"SELECT coluname(s) FROM table1 join table2 ON table1.coluname=table2.coluname\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testSelectOneJoinWithAliase() {\n        String sql = \"SELECT coluname(s) FROM table1 t1 join table2 t2 ON t1.coluname=t2.coluname\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testSelectOneLeftJoin() {\n        String sql = \"SELECT coluname(s) FROM table1 left outer join table2 ON table1.coluname=table2.coluname\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n\n    @Test\n    public void testShouldIgnoreDual() {\n        String sql = \"select * from dual\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n\n    @Test\n    public void testSelectTwoJoinWithAliase() {\n        assertThat(new TableNameParser(SQL_SELECT_THREE_JOIN_WITH_ALIASE).tables()).isEqualTo(asSet(\"colleges\", \"students\", \"results\"));\n    }\n\n\n    @Test\n    public void testInsertWithValues() {\n        String sql = \"INSERT INTO table_name VALUES (value1,value2,value3,...)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table_name\"));\n    }\n\n    @Test\n    public void testInsertComplex() {\n        assertThat(new TableNameParser(SQL_COMPLEX_ONE).tables()).isEqualTo(asSet(\"static_product\", \"DR_BD_PRODUCT\", \"DR_BUNDLE\", \"map_edge_ver\", \"ITEM\", \"DR_BUNDLE_DISCOUNT\"));\n    }\n\n    @Test\n    public void testInsertWithSelect() {\n        String sql = \"INSERT INTO Customers (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"Customers\", \"Suppliers\"));\n    }\n\n    @Test\n    public void testDelete() {\n        String sql = \"DELETE FROM validation_task WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf WHERE conf_name IN ('SaleValidation'))\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"validation_task\", \"validation_conf\"));\n    }\n\n    @Test\n    public void testOracleSpecialDelete() {\n        String sql = \"delete table1 where column_name=xyz\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n    @Test\n    public void testAlter() {\n        String sql = \"ALTER TABLE Persons ADD UNIQUE (P_Id)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"Persons\"));\n    }\n\n    @Test\n    public void testAlter2() {\n        String sql = \"ALTER TABLE table_name MODIFY coluname datatype\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table_name\"));\n    }\n\n    @Test\n    public void testDrop() {\n        String sql = \"DROP table tname;\\n\\r\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"tname\"));\n    }\n\n    @Test\n    public void testDropFunction() {\n        String sql = \"DROP FUNCTION functionName;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n    @Test\n    public void testDropProcedure() {\n        String sql = \"drop procedure procedureName\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n    @Test\n    public void testDropView() {\n        String sql = \"DROP VIEW viewName\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n    @Test\n    public void testDropIndex() {\n        String sql = \"DROP INDEX indexName\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n    @Test\n    public void testUnionAll() {\n        String sql = \"SELECT coluname(s) FROM table1 UNION ALL SELECT coluname(s) FROM table2;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testMerge() {\n        String sql = \"MERGE INTO employees e  USING hr_records h  ON (e.id = h.emp_id) WHEN MATCHED THEN  UPDATE SET e.address = h.address  WHEN NOT MATCHED THEN    INSERT (id, address) VALUES (h.emp_id, h.address);\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"employees\", \"hr_records\"));\n    }\n\n    @Test\n    public void testMergeUsingQuery() {\n        String sql = \"MERGE INTO employees e USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h  ON (e.id = h.emp_id)  WHEN MATCHED THEN  UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"employees\", \"hr_records\"));\n    }\n\n    @Test\n    public void testMergeComplexQuery() {\n        assertThat(new TableNameParser(SQL_MERGE_COMPLEX).tables()).isEqualTo(asSet(\"non_strategy_comps\", \"cf_procedure\", \"struct_doc_Sect_ver\", \"cf_period\", \"cf_component\", \"cf_strategy\", \"tier_basis_ver\", \"non_strategy_details\", \"cf_procedure\", \"non_strat_periods\"));\n    }\n\n    @Test\n    public void testMergeComplexQuery2() {\n        assertThat(new TableNameParser(SQL_MERGE_COMPLEX_TWO).tables()).isEqualTo(asSet(\"cf_procedure_ver\", \"cf_procedure\"));\n    }\n\n    @Test\n    public void testCreateTable() {\n        String sql = \"CREATE TABLE Persons(PersonID int,LastName varchar(255),FirstName varchar(255),Address varchar(255),City varchar(255));\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"Persons\"));\n    }\n\n    @Test\n    public void testCreateGlobalTable() {\n        String sql = \"CREATE GLOBAL TEMPORARY TABLE excl_cust (gen_name VARCHAR2(100),run_date TIMESTAMP(3), item_root_uuid  VARCHAR2(22), owner_member_id  NUMBER(20)) ON COMMIT DELETE ROWS\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"excl_cust\"));\n    }\n\n    @Test\n    public void testCreateIndex() {\n        String sql = \"CREATE INDEX temp_name_idx ON table1(name) NOLOGGING PARALLEL (DEGREE 8);\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n    @Test\n    public void testCreateView() {\n        String sql = \"CREATE VIEW dept AS SELECT * FROM dept;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"dept\"));\n    }\n\n    @Test\n    public void testCreateView2() {\n        String sql = \"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp, dept WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"dept\", \"emp\"));\n    }\n\n    @Test\n    public void testCreateType() {\n        String sql = \"CREATE OR REPLACE TYPE TYPE_NAME IS TABLE OF VARCHAR2(100)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet());\n    }\n\n    @Test\n    public void testUpdateTable() {\n        String sql = \"UPDATE tableName SET column1 = expression1, column2 = expression2\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"tableName\"));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery() {\n        String sql = \"UPDATE table1 SET table1.value = (SELECT table2.CODE FROM table2 WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE='blah' AND EXISTS (SELECT table2.CODE  FROM table2    WHERE table1.value = table2.DESC);\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery2() {\n        String sql = \"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1 INNER JOIN table2 ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\", \"table2\"));\n    }\n\n    @Test\n    public void testUpdateTableSubQueryWithOracleHint() {\n        String sql = \"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"eligible\"));\n    }\n\n    @Test\n    public void testTruncateTable() {\n        String sql = \"truncate table eligible_item\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"eligible_item\"));\n    }\n\n    @Test\n    public void testSqlWithComment() {\n        String sql = \"select * from foo -- this is a comment\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\"));\n    }\n\n    @Test\n    public void testSqlWithCommentContainingKeyword() {\n        String sql = \"select * from foo -- what happens if I say update in a comment\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\"));\n    }\n\n    @Test\n    public void testSqlWithCommentEndingWithKeyword() {\n        String sql = \"select * from foo -- what happens if I end a comment with an update\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\"));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddle() {\n        String sql = \"select * -- I like stars \\n from foo\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\"));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddleAndEnd() {\n        String sql = \"select * -- I like stars \\n from foo -- comment ending with update\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\"));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddle() {\n        String sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\", \"bar\"));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsAndNewlines() {\n        String sql = \"select * -- I like stars \\n from foo f -- I like foo \\n\\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\", \"bar\"));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddleAndEnd() {\n        String sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id -- comment ending with update\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"foo\", \"bar\"));\n    }\n\n    @Test\n    void testSelectForUpdate() {\n        //TODO 暂时解决不能使用的问题,当碰到for update nowait这样的,后面的 nowait 会被当做成表但也不是很影响苗老板的动态表过滤.\n        assertThat(new TableNameParser(\"select * from mp where id = 1 for update\").tables()).isEqualTo(asSet(\"mp\"));\n    }\n\n    @Test\n    public void testOnDuplicateKeyUpdate () {\n        String sql = \"INSERT INTO cf_procedure (_id,password) VALUES ('1','password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword';\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"cf_procedure\"));\n    }\n\n    @Test\n    public void testUpdateIgnore() {\n        String sql = \"update ignore student set name = 'abc' where id = 4\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"student\"));\n\n        sql = \"UPDATE IGNORE student set name = 'abc' where id = 4\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"student\"));\n    }\n\n    @Test\n    public void testInsertIgnore() {\n        String sql = \"INSERT IGNORE INTO student (userid,username) VALUES (2,'swan'),(4,'bear') ;\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"student\"));\n    }\n\n    @Test\n    void testCreateTableIfNotExists() {\n        var sql = \"\"\"\n            CREATE TABLE IF NOT EXISTS `user_info` (\n                `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n                `username` VARCHAR(50) NOT NULL UNIQUE,\n                `email` VARCHAR(100) NOT NULL UNIQUE,\n                `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n            \"\"\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"`user_info`\"));\n    }\n\n    @Test\n    void testCreateUniqueIndex() {\n        var sql = \"CREATE UNIQUE INDEX index_name ON table1 (a, b)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n        sql = \"ALTER TABLE table1 ADD UNIQUE INDEX `a`(`a`)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n    @Test\n    void testCreateFullTextIndex(){\n        var sql = \"CREATE FULLTEXT INDEX index_name ON table1 (a, b)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n        sql = \"ALTER TABLE table1 ADD FULLTEXT INDEX `a`(`a`)\";\n        assertThat(new TableNameParser(sql).tables()).isEqualTo(asSet(\"table1\"));\n    }\n\n\n    private static Collection<String> asSet(String... a) {\n        Set<String> result = new HashSet<>();\n        Collections.addAll(result, a);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-core/src/test/resources/MybatisXMLConfigBuilderTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration\n    PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n    \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n    <mappers>\n        <mapper class=\"com.baomidou.mybatisplus.core.MybatisXMLConfigBuilderTest$EntityMapper\"/>\n    </mappers>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus-core/src/test/resources/com/baomidou/mybatisplus/test/mapper/AMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.AMapper\">\n\n    <cache-ref namespace=\"com.baomidou.mybatisplus.test.mapper.BMapper\"/>\n\n    <resultMap id=\"xxx1\" type=\"com.baomidou.mybatisplus.test.entity.AEntity\">\n        <id column=\"id\" property=\"id\"/>\n        <result column=\"name\" property=\"name\"/>\n    </resultMap>\n\n    <sql id=\"testAsql\">\n        id\n    </sql>\n\n    <select id=\"test\" resultType=\"com.baomidou.mybatisplus.test.entity.AEntity\">\n        select * from test_a where id = 1\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-core/src/test/resources/com/baomidou/mybatisplus/test/mapper/BMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.BMapper\">\n\n    <cache/>\n\n    <sql id=\"testBsql\">\n        name\n    </sql>\n\n    <select id=\"test\" resultType=\"com.baomidou.mybatisplus.test.entity.BEntity\">\n        select * from test_b where id = 1\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-core/src/test/resources/mybatis-config-empty.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration\n        PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n</configuration>\n"
  },
  {
    "path": "mybatis-plus-extension/build.gradle",
    "content": "apply plugin: 'kotlin'\n\ncompileKotlin{\n    kotlinOptions.jvmTarget = \"1.8\"\n}\n\ndependencies {\n    api project(\":mybatis-plus-core\")\n\n    implementation \"${lib.\"kotlin-stdlib-jdk8\"}\"\n    implementation \"${lib.\"kotlin-reflect\"}\"\n    implementation \"${lib.\"slf4j-api\"}\"\n    implementation \"${lib.\"p6spy\"}\"\n    implementation \"${lib.\"jackson\"}\"\n    implementation \"${lib.\"jackson3\"}\"\n    implementation \"${lib.\"fastjson\"}\"\n    implementation \"${lib.\"gson\"}\"\n    implementation \"${lib['mybatis-thymeleaf']}\"\n    implementation \"${lib.'mybatis-velocity'}\"\n    implementation \"${lib.'mybatis-freemarker'}\"\n    testImplementation \"${lib.\"spring-context-support\"}\"\n    testImplementation \"${lib.h2}\"\n    testImplementation \"${lib.mysql}\"\n    testImplementation \"${lib.postgresql}\"\n    testImplementation \"${lib.oracle}\"\n    testImplementation \"${lib.sqlite}\"\n    testImplementation lib.dm as ConfigurableFileTree\n    testImplementation \"${lib.'logback-classic'}\"\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/MybatisMapWrapperFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension;\n\nimport com.baomidou.mybatisplus.extension.handlers.MybatisMapWrapper;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.wrapper.ObjectWrapper;\nimport org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;\n\nimport java.util.Map;\n\n/**\n * 开启返回map结果集的下划线转驼峰\n *\n * <p> Map 的 key 下划线转驼峰 </p>\n * <p>configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());</p>\n *\n * @author yuxiaobin\n * @since 2017-12-19\n */\npublic class MybatisMapWrapperFactory implements ObjectWrapperFactory {\n\n    @Override\n    public boolean hasWrapperFor(Object object) {\n        return object instanceof Map;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {\n        return new MybatisMapWrapper(metaObject, (Map<String, Object>) object);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/activerecord/AbstractModel.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.activerecord;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.session.SqlSession;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * ActiveRecord 模式 CRUD\n * <p>\n * 必须存在对应的原始mapper并继承baseMapper并且可以使用的前提下\n * 才能使用此 AR 模式 !!!\n * </p>\n *\n * @param <T>\n * @author hubin\n * @since 2016-11-06\n */\npublic abstract class AbstractModel<T extends AbstractModel<?>> implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    protected final transient Class<?> entityClass = this.getClass();\n\n    /**\n     * 插入（字段选择插入）\n     */\n    public boolean insert() {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.insert(sqlStatement(SqlMethod.INSERT_ONE), this));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 插入 OR 更新\n     */\n    public boolean insertOrUpdate() {\n        return StringUtils.checkValNull(pkVal()) || Objects.isNull(selectById(pkVal())) ? insert() : updateById();\n    }\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param id 主键ID\n     */\n    public boolean deleteById(Serializable id) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE_BY_ID), id));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据主键删除\n     */\n    public boolean deleteById() {\n        Assert.isFalse(StringUtils.checkValNull(pkVal()), \"deleteById primaryKey is null.\");\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE_BY_ID), this));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 删除记录\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    public boolean delete(Wrapper<T> queryWrapper) {\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);\n        map.put(Constants.WRAPPER, queryWrapper);\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.delete(sqlStatement(SqlMethod.DELETE), map));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 更新（字段选择更新）\n     */\n    public boolean updateById() {\n        Assert.isFalse(StringUtils.checkValNull(pkVal()), \"updateById primaryKey is null.\");\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);\n        map.put(Constants.ENTITY, this);\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.update(sqlStatement(SqlMethod.UPDATE_BY_ID), map));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 执行 SQL 更新\n     *\n     * @param updateWrapper 实体对象封装操作类（可以为 null,里面的 entity 用于生成 where 语句）\n     */\n    public boolean update(Wrapper<T> updateWrapper) {\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(2);\n        map.put(Constants.ENTITY, this);\n        map.put(Constants.WRAPPER, updateWrapper);\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.update(sqlStatement(SqlMethod.UPDATE), map));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 查询所有\n     */\n    public List<T> selectAll() {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据 ID 查询\n     *\n     * @param id 主键ID\n     */\n    public T selectById(Serializable id) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return sqlSession.selectOne(sqlStatement(SqlMethod.SELECT_BY_ID), id);\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据主键查询\n     */\n    public T selectById() {\n        Assert.isFalse(StringUtils.checkValNull(pkVal()), \"selectById primaryKey is null.\");\n        return selectById(pkVal());\n    }\n\n    /**\n     * 查询总记录数\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    public List<T> selectList(Wrapper<T> queryWrapper) {\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);\n        map.put(Constants.WRAPPER, queryWrapper);\n        SqlSession sqlSession = sqlSession();\n        try {\n            return sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST), map);\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    public T selectOne(Wrapper<T> queryWrapper) {\n        return SqlHelper.getObject(() -> LogFactory.getLog(this.entityClass), selectList(queryWrapper));\n    }\n\n    /**\n     * 翻页查询\n     *\n     * @param page         翻页查询条件\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper) {\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(2);\n        map.put(Constants.WRAPPER, queryWrapper);\n        map.put(\"page\", page);\n        SqlSession sqlSession = sqlSession();\n        try {\n            page.setRecords(sqlSession.selectList(sqlStatement(SqlMethod.SELECT_LIST), map));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n        return page;\n    }\n\n    /**\n     * 查询总数\n     *\n     * @param queryWrapper 实体对象封装操作类（可以为 null）\n     */\n    public long selectCount(Wrapper<T> queryWrapper) {\n        Map<String, Object> map = CollectionUtils.newHashMapWithExpectedSize(1);\n        map.put(Constants.WRAPPER, queryWrapper);\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retCount(sqlSession.<Long>selectOne(sqlStatement(SqlMethod.SELECT_COUNT), map));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 获取Session 默认自动提交\n     */\n    protected SqlSession sqlSession() {\n        return SqlHelper.sqlSession(this.entityClass);\n    }\n\n    /**\n     * 获取SqlStatement\n     *\n     * @param sqlMethod sqlMethod\n     */\n    protected String sqlStatement(SqlMethod sqlMethod) {\n        return sqlStatement(sqlMethod.getMethod());\n    }\n\n    /**\n     * 获取SqlStatement\n     *\n     * @param sqlMethod sqlMethod\n     */\n    protected String sqlStatement(String sqlMethod) {\n        //无法确定对应的mapper，只能用注入时候绑定的了。\n        return SqlHelper.table(this.entityClass).getSqlStatement(sqlMethod);\n    }\n\n    /**\n     * 主键值\n     */\n    public Serializable pkVal() {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);\n        return (Serializable) tableInfo.getPropertyValue(this, tableInfo.getKeyProperty());\n    }\n\n    /**\n     * 释放sqlSession\n     *\n     * @param sqlSession session\n     */\n    protected void closeSqlSession(SqlSession sqlSession) {\n        CompatibleHelper.getCompatibleSet().closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.entityClass));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/activerecord/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Active Record 数据模型操作\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.activerecord;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/AbstractChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Compare;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Func;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Join;\nimport com.baomidou.mybatisplus.core.conditions.interfaces.Nested;\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiPredicate;\nimport java.util.function.Consumer;\n\n/**\n * 所有包装类都继承此抽象类,此抽象类代理了大部分生成 where 条件的方法\n * <li> 泛型: Children ,表示子类 </li>\n * <li> 泛型: Param ,表示子类所包装的具体 Wrapper 类型 </li>\n *\n * @author miemie\n * @since 2018-12-19\n */\n@SuppressWarnings({\"unchecked\"})\npublic abstract class AbstractChainWrapper<T, R, Children extends AbstractChainWrapper<T, R, Children, Param>, Param extends AbstractWrapper<T, R, Param>>\n    extends Wrapper<T> implements Compare<Children, R>, Func<Children, R>, Join<Children>, Nested<Param, Children> {\n\n    protected final Children typedThis = (Children) this;\n    /**\n     * 子类所包装的具体 Wrapper 类型\n     */\n    protected Param wrapperChildren;\n\n    /**\n     * 必须的构造函数\n     */\n    public AbstractChainWrapper() {\n    }\n\n    public AbstractWrapper<T, R, Param> getWrapper() {\n        return wrapperChildren;\n    }\n\n    public Children setEntity(T entity) {\n        getWrapper().setEntity(entity);\n        return typedThis;\n    }\n\n    public Children setEntityClass(Class<T> entityClass) {\n        getWrapper().setEntityClass(entityClass);\n        return typedThis;\n    }\n\n    @Override\n    public <V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull) {\n        getWrapper().allEq(condition, params, null2IsNull);\n        return typedThis;\n    }\n\n    @Override\n    public <V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {\n        getWrapper().allEq(condition, filter, params, null2IsNull);\n        return typedThis;\n    }\n\n    @Override\n    public Children eq(boolean condition, R column, Object val) {\n        getWrapper().eq(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children ne(boolean condition, R column, Object val) {\n        getWrapper().ne(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children gt(boolean condition, R column, Object val) {\n        getWrapper().gt(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children ge(boolean condition, R column, Object val) {\n        getWrapper().ge(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children lt(boolean condition, R column, Object val) {\n        getWrapper().lt(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children le(boolean condition, R column, Object val) {\n        getWrapper().le(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children between(boolean condition, R column, Object val1, Object val2) {\n        getWrapper().between(condition, column, val1, val2);\n        return typedThis;\n    }\n\n    @Override\n    public Children notBetween(boolean condition, R column, Object val1, Object val2) {\n        getWrapper().notBetween(condition, column, val1, val2);\n        return typedThis;\n    }\n\n    @Override\n    public Children like(boolean condition, R column, Object val) {\n        getWrapper().like(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children likeLeft(boolean condition, R column, Object val) {\n        getWrapper().likeLeft(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children likeRight(boolean condition, R column, Object val) {\n        getWrapper().likeRight(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children notLike(boolean condition, R column, Object val) {\n        getWrapper().notLike(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children notLikeLeft(boolean condition, R column, Object val) {\n        getWrapper().notLikeLeft(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children notLikeRight(boolean condition, R column, Object val) {\n        getWrapper().notLikeRight(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public Children isNull(boolean condition, R column) {\n        getWrapper().isNull(condition, column);\n        return typedThis;\n    }\n\n    @Override\n    public Children isNotNull(boolean condition, R column) {\n        getWrapper().isNotNull(condition, column);\n        return typedThis;\n    }\n\n    @Override\n    public Children in(boolean condition, R column, Collection<?> coll) {\n        getWrapper().in(condition, column, coll);\n        return typedThis;\n    }\n\n    @Override\n    public Children in(boolean condition, R column, Object... values) {\n        getWrapper().in(condition, column, values);\n        return typedThis;\n    }\n\n    @Override\n    public Children notIn(boolean condition, R column, Collection<?> coll) {\n        getWrapper().notIn(condition, column, coll);\n        return typedThis;\n    }\n\n    @Override\n    public Children notIn(boolean condition, R column, Object... values) {\n        getWrapper().notIn(condition, column, values);\n        return typedThis;\n    }\n\n    @Override\n    public Children eqSql(boolean condition, R column, String eqValue) {\n        getWrapper().eqSql(condition, column, eqValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children inSql(boolean condition, R column, String inValue) {\n        getWrapper().inSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children gtSql(boolean condition, R column, String inValue) {\n        getWrapper().gtSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children geSql(boolean condition, R column, String inValue) {\n        getWrapper().geSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children ltSql(boolean condition, R column, String inValue) {\n        getWrapper().ltSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children leSql(boolean condition, R column, String inValue) {\n        getWrapper().leSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    public Children notInSql(boolean condition, R column, String inValue) {\n        getWrapper().notInSql(condition, column, inValue);\n        return typedThis;\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children groupBy(boolean condition, R column, R... columns) {\n        return doGroupBy(condition, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    public Children groupBy(boolean condition, R column, List<R> columns) {\n        return doGroupBy(condition, column, columns);\n    }\n\n    @Override\n    public Children groupBy(boolean condition, R column) {\n        return doGroupBy(condition, column, null);\n    }\n\n    @Override\n    public Children groupBy(boolean condition, List<R> columns) {\n        return doGroupBy(condition, null, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderBy(boolean condition, boolean isAsc, R column, R... columns) {\n        return orderBy(condition, isAsc, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, R column, List<R> columns) {\n        return doOrderBy(condition, isAsc, column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children groupBy(R column, R... columns) {\n        return doGroupBy(true, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByAsc(R column, R... columns) {\n        return orderByAsc(true, column, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByAsc(boolean condition, R column, R... columns) {\n        return doOrderByAsc(condition, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByDesc(boolean condition, R column, R... columns) {\n        return doOrderByDesc(condition, column, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final Children orderByDesc(R column, R... columns) {\n        return orderByDesc(true, column, columns);\n    }\n\n    // --------------  新增重写方法开始----------------\n    protected Children doOrderByDesc(boolean condition, R column, List<R> columns) {\n        return doOrderBy(condition, false, column, columns);\n    }\n\n    protected Children doOrderByAsc(boolean condition, R column, List<R> columns) {\n        return doOrderBy(condition, true, column, columns);\n    }\n\n    protected Children doOrderBy(boolean condition, boolean isAsc, R column, List<R> columns) {\n        getWrapper().doOrderBy(condition, isAsc, column, columns);\n        return typedThis;\n    }\n\n    protected Children doGroupBy(boolean condition, R column, List<R> columns) {\n        getWrapper().doGroupBy(condition, column, columns);\n        return typedThis;\n    }\n\n\n    // --------------  新增重写方法结束----------------\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, R column) {\n        return doOrderBy(condition, isAsc, column, null);\n    }\n\n    @Override\n    public Children orderBy(boolean condition, boolean isAsc, List<R> columns) {\n        return doOrderBy(condition, isAsc, null, columns);\n    }\n\n    @Override\n    public Children having(boolean condition, String sqlHaving, Object... params) {\n        getWrapper().having(condition, sqlHaving, params);\n        return typedThis;\n    }\n\n    @Override\n    public Children func(boolean condition, Consumer<Children> consumer) {\n        if (condition) {\n            consumer.accept(typedThis);\n        }\n        return typedThis;\n    }\n\n    @Override\n    public Children or(boolean condition) {\n        getWrapper().or(condition);\n        return typedThis;\n    }\n\n    @Override\n    public Children apply(boolean condition, String applySql, Object... values) {\n        getWrapper().apply(condition, applySql, values);\n        return typedThis;\n    }\n\n    @Override\n    public Children last(boolean condition, String lastSql) {\n        getWrapper().last(condition, lastSql);\n        return typedThis;\n    }\n\n    @Override\n    public Children comment(boolean condition, String comment) {\n        getWrapper().comment(condition, comment);\n        return typedThis;\n    }\n\n    @Override\n    public Children first(boolean condition, String firstSql) {\n        getWrapper().first(condition, firstSql);\n        return typedThis;\n    }\n\n    @Override\n    public Children exists(boolean condition, String existsSql, Object... values) {\n        getWrapper().exists(condition, existsSql, values);\n        return typedThis;\n    }\n\n    @Override\n    public Children notExists(boolean condition, String existsSql, Object... values) {\n        getWrapper().notExists(condition, existsSql, values);\n        return typedThis;\n    }\n\n    @Override\n    public Children and(boolean condition, Consumer<Param> consumer) {\n        getWrapper().and(condition, consumer);\n        return typedThis;\n    }\n\n    @Override\n    public Children or(boolean condition, Consumer<Param> consumer) {\n        getWrapper().or(condition, consumer);\n        return typedThis;\n    }\n\n    @Override\n    public Children nested(boolean condition, Consumer<Param> consumer) {\n        getWrapper().nested(condition, consumer);\n        return typedThis;\n    }\n\n    @Override\n    public Children not(boolean condition, Consumer<Param> consumer) {\n        getWrapper().not(condition, consumer);\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSegment() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSegment\");\n    }\n\n    @Override\n    public String getSqlFirst() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlFirst\");\n    }\n\n    @Override\n    public String getSqlSelect() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSelect\");\n    }\n\n    @Override\n    public String getSqlSet() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSet\");\n    }\n\n    @Override\n    public String getSqlComment() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlComment\");\n    }\n\n    @Override\n    public String getTargetSql() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getTargetSql\");\n    }\n\n    @Override\n    public T getEntity() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getEntity\");\n    }\n\n    @Override\n    public MergeSegments getExpression() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getExpression\");\n    }\n\n    @Override\n    public String getCustomSqlSegment() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getCustomSqlSegment\");\n    }\n\n    @Override\n    public void clear() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"clear\");\n    }\n\n    @Override\n    protected Object clone() throws CloneNotSupportedException {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"clone\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/ChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\n\n/**\n * 此接口没特殊意义,只是为了减少实现类的代码量,主要在 AbstractChainWrapper 抽象类上实现\n * <p>以及 继承该接口的子接口能直接获取到 BaseMapper 和相应的 Wrapper</p>\n *\n * @author miemie\n * @since 2018-12-19\n */\npublic interface ChainWrapper<T> {\n\n    /**\n     * 获取 BaseMapper\n     *\n     * @return BaseMapper\n     */\n    BaseMapper<T> getBaseMapper();\n\n    /**\n     * 获取最终拿去执行的 Wrapper\n     *\n     * @return Wrapper\n     */\n    Wrapper<T> getWrapper();\n\n    /**\n     * 获取当前实体Class\n     *\n     * @return Class\n     */\n    Class<T> getEntityClass();\n\n    /**\n     * 执行baseMapper操作，有baseMapper操作时使用baseMapper，没有时通过entityClass获取baseMapper，再使用\n     *\n     * @param function 操作\n     * @param <R>      返回值\n     * @return 结果\n     */\n    default <R> R execute(SFunction<BaseMapper<T>, R> function) {\n        BaseMapper<T> baseMapper = getBaseMapper();\n        if (baseMapper != null) {\n            return function.apply(baseMapper);\n        }\n        return SqlHelper.execute(getEntityClass(), function);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/query/ChainQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.query;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.conditions.ChainWrapper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\n\n/**\n * 具有查询方法的定义\n *\n * @author miemie\n * @since 2018-12-19\n */\npublic interface ChainQuery<T> extends ChainWrapper<T> {\n\n    /**\n     * 获取集合\n     *\n     * @return 集合\n     */\n    default List<T> list() {\n        return execute(mapper -> mapper.selectList(getWrapper()));\n    }\n\n    /**\n     * 获取集合\n     *\n     * @param page 分页条件\n     * @return 集合记录\n     * @since 3.5.3.2\n     */\n    default List<T> list(IPage<T> page) {\n        return execute(mapper -> mapper.selectList(page, getWrapper()));\n    }\n\n    /**\n     * 获取单个\n     *\n     * @return 单个\n     */\n    default T one() {\n        return execute(mapper -> mapper.selectOne(getWrapper()));\n    }\n\n    /**\n     * 获取单个\n     *\n     * @return 单个\n     * @since 3.3.0\n     */\n    default Optional<T> oneOpt() {\n        return Optional.ofNullable(one());\n    }\n\n    /**\n     * 获取 count\n     *\n     * @return count\n     */\n    default Long count() {\n        return execute(mapper -> SqlHelper.retCount(mapper.selectCount(getWrapper())));\n    }\n\n    /**\n     * 判断数据是否存在\n     *\n     * @return true 存在 false 不存在\n     */\n    default boolean exists() {\n        return this.count() > 0;\n    }\n\n    /**\n     * 获取分页数据\n     *\n     * @param page 分页条件\n     * @return 分页数据\n     */\n    default <E extends IPage<T>> E page(E page) {\n        return execute(mapper -> mapper.selectPage(page, getWrapper()));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/query/LambdaQueryChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.query;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.query.Query;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * @author miemie\n * @since 2018-12-19\n */\n@SuppressWarnings({\"serial\"})\npublic class LambdaQueryChainWrapper<T> extends AbstractChainWrapper<T, SFunction<T, ?>, LambdaQueryChainWrapper<T>, LambdaQueryWrapper<T>>\n    implements ChainQuery<T>, Query<LambdaQueryChainWrapper<T>, T, SFunction<T, ?>> {\n\n    private final BaseMapper<T> baseMapper;\n\n    public LambdaQueryChainWrapper(BaseMapper<T> baseMapper) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = new LambdaQueryWrapper<>();\n    }\n\n    public LambdaQueryChainWrapper(Class<T> entityClass) {\n        super();\n        this.baseMapper = null;\n        super.wrapperChildren = new LambdaQueryWrapper<>(entityClass);\n    }\n\n    public LambdaQueryChainWrapper(BaseMapper<T> baseMapper, T entity) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = new LambdaQueryWrapper<>(entity);\n    }\n\n    public LambdaQueryChainWrapper(BaseMapper<T> baseMapper, Class<T> entityClass) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = new LambdaQueryWrapper<>(entityClass);\n    }\n\n    public LambdaQueryChainWrapper(BaseMapper<T> baseMapper, LambdaQueryWrapper<T> wrapperChildren) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = wrapperChildren;\n    }\n\n    @Override\n    public LambdaQueryChainWrapper<T> select(boolean condition, List<SFunction<T, ?>> columns) {\n        return doSelect(condition, columns);\n    }\n\n    @Override\n    @SafeVarargs\n    public final LambdaQueryChainWrapper<T> select(SFunction<T, ?>... columns) {\n        return doSelect(true, CollectionUtils.toList(columns));\n    }\n\n    @Override\n    @SafeVarargs\n    public final LambdaQueryChainWrapper<T> select(boolean condition, SFunction<T, ?>... columns) {\n        return doSelect(condition, CollectionUtils.toList(columns));\n    }\n\n    /**\n     * @since 3.5.4\n     */\n    protected LambdaQueryChainWrapper<T> doSelect(boolean condition, List<SFunction<T, ?>> columns) {\n        wrapperChildren.select(condition, columns);\n        return typedThis;\n    }\n\n    @Override\n    public LambdaQueryChainWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {\n        wrapperChildren.select(entityClass, predicate);\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSelect() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSelect\");\n    }\n\n    @Override\n    public BaseMapper<T> getBaseMapper() {\n        return baseMapper;\n    }\n\n    @Override\n    public Class<T> getEntityClass() {\n        return super.wrapperChildren.getEntityClass();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/query/QueryChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.query;\n\nimport com.baomidou.mybatisplus.core.conditions.query.Query;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * Query Chain Wrapper\n *\n * @author miemie\n * @since 2018-12-19\n */\n@SuppressWarnings({\"serial\"})\npublic class QueryChainWrapper<T> extends AbstractChainWrapper<T, String, QueryChainWrapper<T>, QueryWrapper<T>>\n    implements ChainQuery<T>, Query<QueryChainWrapper<T>, T, String> {\n\n    private final BaseMapper<T> baseMapper;\n    private final Class<T> entityClass;\n\n    public QueryChainWrapper(BaseMapper<T> baseMapper) {\n        super();\n        this.baseMapper = baseMapper;\n        this.entityClass = null;\n        super.wrapperChildren = new QueryWrapper<>();\n    }\n\n    public QueryChainWrapper(Class<T> entityClass) {\n        super();\n        this.baseMapper = null;\n        this.entityClass = entityClass;\n        super.wrapperChildren = new QueryWrapper<>();\n    }\n\n    @Override\n    public QueryChainWrapper<T> select(boolean condition, List<String> columns) {\n        wrapperChildren.select(condition, columns);\n        return typedThis;\n    }\n\n    @Override\n    public QueryChainWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {\n        wrapperChildren.select(entityClass, predicate);\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSelect() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSelect\");\n    }\n\n    @Override\n    public BaseMapper<T> getBaseMapper() {\n        return baseMapper;\n    }\n\n    /**\n     * 获取当前实体Class\n     *\n     * @return Class\n     */\n    @Override\n    public Class<T> getEntityClass() {\n        return entityClass;\n    }\n\n\n    public LambdaQueryChainWrapper<T> lambda(){\n        return new LambdaQueryChainWrapper<>(\n            baseMapper,\n            wrapperChildren.lambda()\n        );\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/update/ChainUpdate.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.update;\n\nimport com.baomidou.mybatisplus.extension.conditions.ChainWrapper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\n\n/**\n * 具有更新方法的定义\n *\n * @author miemie\n * @since 2018-12-19\n */\npublic interface ChainUpdate<T> extends ChainWrapper<T> {\n\n    /**\n     * 更新数据\n     * <p>此方法无法进行自动填充,如需自动填充请使用{@link #update(Object)}</p>\n     *\n     * @return 是否成功\n     */\n    default boolean update() {\n        return update(null);\n    }\n\n    /**\n     * 更新数据\n     *\n     * @param entity 实体类(当entity为空时无法进行自动填充)\n     * @return 是否成功\n     */\n    default boolean update(T entity) {\n        return execute(mapper -> SqlHelper.retBool(mapper.update(entity, getWrapper())));\n    }\n\n    /**\n     * 删除数据\n     *\n     * @return 是否成功\n     */\n    default boolean remove() {\n        return execute(mapper -> SqlHelper.retBool(mapper.delete(getWrapper())));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/update/LambdaUpdateChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.update;\n\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.Update;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;\n\n/**\n * Lambda Update Chain Wrapper\n *\n * @author miemie\n * @since 2018-12-19\n */\npublic class LambdaUpdateChainWrapper<T> extends AbstractChainWrapper<T, SFunction<T, ?>, LambdaUpdateChainWrapper<T>, LambdaUpdateWrapper<T>>\n    implements ChainUpdate<T>, Update<LambdaUpdateChainWrapper<T>, SFunction<T, ?>> {\n\n    private final BaseMapper<T> baseMapper;\n\n    public LambdaUpdateChainWrapper(BaseMapper<T> baseMapper) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = new LambdaUpdateWrapper<>();\n    }\n\n    public LambdaUpdateChainWrapper(Class<T> entityClass) {\n        super();\n        this.baseMapper = null;\n        super.wrapperChildren = new LambdaUpdateWrapper<>(entityClass);\n    }\n\n    public LambdaUpdateChainWrapper(BaseMapper<T> baseMapper, LambdaUpdateWrapper<T> wrapperChildren) {\n        super();\n        this.baseMapper = baseMapper;\n        super.wrapperChildren = wrapperChildren;\n    }\n\n    @Override\n    public LambdaUpdateChainWrapper<T> set(boolean condition, SFunction<T, ?> column, Object val, String mapping) {\n        wrapperChildren.set(condition, column, val, mapping);\n        return typedThis;\n    }\n\n    @Override\n    public LambdaUpdateChainWrapper<T> setSql(boolean condition, String setSql, Object... params) {\n        wrapperChildren.setSql(condition, setSql, params);\n        return typedThis;\n    }\n\n\n    /**\n     * 字段自增变量 val 值\n     *\n     * @param condition 条件\n     * @param column    字段\n     * @param val       值\n     * @return this\n     * @since 3.5.6\n     */\n    @Override\n    public LambdaUpdateChainWrapper<T> setIncrBy(boolean condition, SFunction<T, ?> column, Number val) {\n        wrapperChildren.setIncrBy(condition, column, val);\n        return typedThis;\n    }\n\n    /**\n     * 字段自减变量 val 值\n     *\n     * @param condition 条件\n     * @param column    字段\n     * @param val       值\n     * @return this\n     * @since 3.5.6\n     */\n    @Override\n    public LambdaUpdateChainWrapper<T> setDecrBy(boolean condition, SFunction<T, ?> column, Number val) {\n        wrapperChildren.setDecrBy(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public BaseMapper<T> getBaseMapper() {\n        return baseMapper;\n    }\n\n    @Override\n    public Class<T> getEntityClass() {\n        return super.wrapperChildren.getEntityClass();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/conditions/update/UpdateChainWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.conditions.update;\n\nimport com.baomidou.mybatisplus.core.conditions.update.Update;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;\n\n/**\n * Update Chain Wrapper\n *\n * @author miemie\n * @since 2018-12-19\n */\n@SuppressWarnings({\"serial\"})\npublic class UpdateChainWrapper<T> extends AbstractChainWrapper<T, String, UpdateChainWrapper<T>, UpdateWrapper<T>>\n    implements ChainUpdate<T>, Update<UpdateChainWrapper<T>, String> {\n\n    private final BaseMapper<T> baseMapper;\n    private final Class<T> entityClass;\n\n    public UpdateChainWrapper(BaseMapper<T> baseMapper) {\n        super();\n        this.baseMapper = baseMapper;\n        this.entityClass = null;\n        super.wrapperChildren = new UpdateWrapper<>();\n    }\n\n    public UpdateChainWrapper(Class<T> entityClass) {\n        super();\n        this.baseMapper = null;\n        this.entityClass = entityClass;\n        super.wrapperChildren = new UpdateWrapper<>();\n    }\n\n    @Override\n    public UpdateChainWrapper<T> set(boolean condition, String column, Object val, String mapping) {\n        wrapperChildren.set(condition, column, val, mapping);\n        return typedThis;\n    }\n\n    @Override\n    public UpdateChainWrapper<T> setSql(boolean condition, String setSql, Object... params) {\n        wrapperChildren.setSql(condition, setSql, params);\n        return typedThis;\n    }\n\n    @Override\n    public UpdateChainWrapper<T> setIncrBy(boolean condition, String column, Number val) {\n        wrapperChildren.setIncrBy(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public UpdateChainWrapper<T> setDecrBy(boolean condition, String column, Number val) {\n        wrapperChildren.setDecrBy(condition, column, val);\n        return typedThis;\n    }\n\n    @Override\n    public String getSqlSet() {\n        throw ExceptionUtils.mpe(\"can not use this method for \\\"%s\\\"\", \"getSqlSet\");\n    }\n\n    @Override\n    public BaseMapper<T> getBaseMapper() {\n        return baseMapper;\n    }\n\n    @Override\n    public Class<T> getEntityClass() {\n        return entityClass;\n    }\n\n    public LambdaUpdateChainWrapper<T> lambda(){\n        return new LambdaUpdateChainWrapper<>(\n            baseMapper,\n            wrapperChildren.lambda()\n        );\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.ddl.history.*;\nimport com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.jdbc.ScriptRunner;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport javax.sql.DataSource;\nimport java.io.*;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\n/**\n * DDL 辅助类\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic class DdlHelper {\n\n    private static final Log LOG = LogFactory.getLog(DdlHelper.class);\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator DDL 生成器\n     * @param connection   数据库连接 (自行控制回收)\n     * @param sqlFiles     SQL 文件列表\n     * @param autoCommit   是否自动提交事务\n     * @throws SQLException SQLException\n     */\n    public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List<String> sqlFiles, boolean autoCommit) throws SQLException {\n        runScript(ddlGenerator, connection, sqlFiles, autoCommit, DdlScriptErrorHandler.PrintlnLogErrorHandler.INSTANCE);\n    }\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param connection            数据库连接 (自行控制回收)\n     * @param sqlFiles              SQL 文件列表\n     * @param autoCommit            是否自动提交事务\n     * @param ddlScriptErrorHandler 错误处理器\n     * @throws SQLException SQLException\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List<String> sqlFiles,\n                                 boolean autoCommit, DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        runScript(ddlGenerator, connection, sqlFiles, null, autoCommit, ddlScriptErrorHandler);\n    }\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param connection            数据库连接 (自行控制回收)\n     * @param sqlFiles              SQL 文件列表\n     * @param scriptRunnerConsumer  自定义 ScriptRunner 函数\n     * @param ddlScriptErrorHandler 错误处理器\n     * @throws SQLException SQLException\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List<String> sqlFiles,\n                                 Consumer<ScriptRunner> scriptRunnerConsumer, DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        runScript(ddlGenerator, connection, sqlFiles, scriptRunnerConsumer, false, ddlScriptErrorHandler);\n    }\n\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param connection            数据库连接 (自行控制回收)\n     * @param sqlFiles              SQL 文件列表\n     * @param scriptRunnerConsumer  自定义 ScriptRunner 函数\n     * @param autoCommit            是否自动提交事务\n     * @param ddlScriptErrorHandler 错误处理器\n     * @throws SQLException SQLException\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator, Connection connection, List<String> sqlFiles, Consumer<ScriptRunner> scriptRunnerConsumer,\n                                 boolean autoCommit, DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        final String jdbcUrl = connection.getMetaData().getURL();\n        SqlRunner sqlRunner = new SqlRunner(connection);\n        ScriptRunner scriptRunner = getScriptRunner(connection, autoCommit);\n        if (scriptRunnerConsumer != null) {\n            scriptRunnerConsumer.accept(scriptRunner);\n        }\n        if (null == ddlGenerator) {\n            ddlGenerator = getDdlGenerator(jdbcUrl);\n        }\n        if (!ddlGenerator.existTable(connection)) {\n            scriptRunner.runScript(new StringReader(ddlGenerator.createDdlHistory()));\n        }\n        // 执行 SQL 脚本\n        for (String sqlFile : sqlFiles) {\n            try {\n                List<Map<String, Object>> objectMap = sqlRunner.selectAll(ddlGenerator.selectDdlHistory(sqlFile, StringPool.SQL));\n                if (null == objectMap || objectMap.isEmpty()) {\n                    if (LOG.isDebugEnabled()) {\n                        LOG.debug(\"Run script file: \" + sqlFile);\n                    }\n                    String[] sqlFileArr = sqlFile.split(StringPool.HASH);\n                    if (Objects.equals(2, sqlFileArr.length)) {\n                        // 命令间的分隔符\n                        scriptRunner.setDelimiter(sqlFileArr[1]);\n                        // 原始文件路径\n                        sqlFile = sqlFileArr[0];\n                    } else {\n                        scriptRunner.setDelimiter(StringPool.SEMICOLON);\n                    }\n                    File file = new File(sqlFile);\n                    if (file.exists()) {\n                        scriptRunner.runScript(new FileReader(file));\n                    } else {\n                        scriptRunner.runScript(new InputStreamReader(getInputStream(sqlFile)));\n                    }\n                    sqlRunner.insert(ddlGenerator.insertDdlHistory(sqlFile, StringPool.SQL, getNowTime()));\n                }\n            } catch (Exception e) {\n                if (ddlScriptErrorHandler != null) {\n                    ddlScriptErrorHandler.handle(sqlFile, e);\n                }\n            }\n        }\n    }\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator DDL 生成器\n     * @param dataSource   数据源\n     * @param sqlFiles     SQL 文件列表\n     * @param autoCommit   是否自动提交事务\n     * @see #runScript(IDdlGenerator, Connection, List, boolean)\n     * @see #runScript(IDdlGenerator, DataSource, List, boolean, DdlScriptErrorHandler)\n     * @deprecated 3.5.11 方法会吞掉所有异常,建议自行处理.\n     */\n    @Deprecated\n    public static void runScript(IDdlGenerator ddlGenerator, DataSource dataSource, List<String> sqlFiles, boolean autoCommit) {\n        try (Connection connection = dataSource.getConnection()) {\n            runScript(ddlGenerator, connection, sqlFiles, autoCommit);\n        } catch (Exception e) {\n            LOG.error(\"Run script error: \", e);\n        }\n    }\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param dataSource            数据源\n     * @param sqlFiles              SQL 文件列表\n     * @param autoCommit            是否自动提交事务\n     * @param ddlScriptErrorHandler 错误处理器\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator,\n                                 DataSource dataSource, List<String> sqlFiles, boolean autoCommit,\n                                 DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        try (Connection connection = dataSource.getConnection()) {\n            runScript(ddlGenerator, connection, sqlFiles, autoCommit, ddlScriptErrorHandler);\n        }\n    }\n\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param dataSource            数据源\n     * @param sqlFiles              SQL 文件列表\n     * @param scriptRunnerConsumer  自定义 ScriptRunner 处理函数\n     * @param ddlScriptErrorHandler 错误处理器\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator,\n                                 DataSource dataSource, List<String> sqlFiles, Consumer<ScriptRunner> scriptRunnerConsumer,\n                                 DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        try (Connection connection = dataSource.getConnection()) {\n            runScript(ddlGenerator, connection, sqlFiles, scriptRunnerConsumer, false, ddlScriptErrorHandler);\n        }\n    }\n\n    /**\n     * 运行 SQL 脚本文件\n     *\n     * @param ddlGenerator          DDL 生成器\n     * @param dataSource            数据源\n     * @param sqlFiles              SQL 文件列表\n     * @param scriptRunnerConsumer  自定义 ScriptRunner 处理函数\n     * @param ddlScriptErrorHandler 错误处理器\n     * @since 3.5.11\n     */\n    public static void runScript(IDdlGenerator ddlGenerator, DataSource dataSource, List<String> sqlFiles,\n                                 Consumer<ScriptRunner> scriptRunnerConsumer, boolean autoCommit, DdlScriptErrorHandler ddlScriptErrorHandler) throws SQLException {\n        try (Connection connection = dataSource.getConnection()) {\n            runScript(ddlGenerator, connection, sqlFiles, scriptRunnerConsumer, autoCommit, ddlScriptErrorHandler);\n        }\n    }\n\n    public static InputStream getInputStream(String path) throws Exception {\n        return Resources.getResourceAsStream(path);\n    }\n\n    protected static String getNowTime() {\n        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(\"yyyyMMddHHmm\"));\n    }\n\n    public static ScriptRunner getScriptRunner(Connection connection, boolean autoCommit) {\n        ScriptRunner scriptRunner = new ScriptRunner(connection);\n        scriptRunner.setAutoCommit(autoCommit);\n        scriptRunner.setEscapeProcessing(false);\n        scriptRunner.setRemoveCRs(true);\n        scriptRunner.setStopOnError(true);\n        scriptRunner.setFullLineDelimiter(false);\n        return scriptRunner;\n    }\n\n    protected static IDdlGenerator getDdlGenerator(String jdbcUrl) throws RuntimeException {\n        DbType dbType = JdbcUtils.getDbType(jdbcUrl);\n        // mysql same type\n        if (dbType.mysqlSameType()) {\n            return MysqlDdlGenerator.newInstance();\n        }\n        // oracle same type\n        else if (dbType.oracleSameType()) {\n            return OracleDdlGenerator.newInstance();\n        } else if (DbType.SQLITE == dbType) {\n            return SQLiteDdlGenerator.newInstance();\n        }\n        // postgresql same type\n        else if (dbType.postgresqlSameType()) {\n            return PostgreDdlGenerator.newInstance();\n        }\n        throw new RuntimeException(\"Unsupported database type: \" + jdbcUrl);\n    }\n\n    public static String getDatabase(String jdbcUrl) {\n        String[] urlArr = jdbcUrl.split(\"://\");\n        if (urlArr.length == 2) {\n            String[] dataArr = urlArr[1].split(\"/\");\n            if (dataArr.length > 1) {\n                return dataArr[1].split(\"\\\\?\")[0];\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlScript.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;\nimport org.apache.ibatis.datasource.unpooled.UnpooledDataSource;\nimport org.apache.ibatis.jdbc.ScriptRunner;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport javax.sql.DataSource;\nimport java.io.Reader;\nimport java.io.StringReader;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * Ddl 脚本执行\n *\n * @author hubin\n * @since 2021-07-23\n */\npublic class DdlScript {\n\n    private static final Log LOG = LogFactory.getLog(DdlScript.class);\n\n    /**\n     * 数据源\n     */\n    private final DataSource dataSource;\n\n\n    /**\n     * DDL生成器\n     *\n     * @deprecated 3.5.11 区分职责,如果需要DDL版本控制请直接使用{@link DdlHelper}\n     */\n    @Deprecated\n    private IDdlGenerator ddlGenerator;\n\n    /**\n     * 是否自动提交\n     */\n    private boolean autoCommit;\n\n    /**\n     * 自定义脚本运行器\n     *\n     * @since 3.5.11\n     */\n    private Consumer<ScriptRunner> scriptRunnerConsumer;\n\n    /**\n     * 非池化执行 (非自动提交)\n     *\n     * @since 3.5.11\n     */\n    public DdlScript(String driverClassName, String url, String user, String password) {\n        this(driverClassName, url, user, password, false);\n    }\n\n    /**\n     * 非池化执行\n     *\n     * @since 3.5.11\n     */\n    public DdlScript(String driverClassName, String url, String user, String password, boolean autoCommit) {\n        this.dataSource = new UnpooledDataSource(driverClassName, url, user, password);\n        this.autoCommit = autoCommit;\n    }\n\n    /**\n     * 创建脚本执行器\n     *\n     * @param dataSource 数据源\n     */\n    public DdlScript(DataSource dataSource) {\n        this.dataSource = dataSource;\n    }\n\n    /**\n     * 创建脚本执行器\n     *\n     * @param dataSource   数据源\n     * @param ddlGenerator DDL生成器\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public DdlScript(DataSource dataSource, IDdlGenerator ddlGenerator) {\n        this(dataSource, ddlGenerator, false);\n    }\n\n    /**\n     * 创建脚本执行器\n     *\n     * @param dataSource   数据源\n     * @param ddlGenerator DDL生成器\n     * @param autoCommit   是否自动提交\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public DdlScript(DataSource dataSource, IDdlGenerator ddlGenerator, boolean autoCommit) {\n        this.dataSource = dataSource;\n        this.ddlGenerator = ddlGenerator;\n        this.autoCommit = autoCommit;\n    }\n\n    /**\n     * 执行 SQL 脚本文件\n     *\n     * @param sqlFiles SQL 脚本文件列表\n     * @see DdlHelper#runScript(IDdlGenerator, DataSource, List, boolean)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public void run(List<String> sqlFiles) {\n        this.run(sqlFiles, this.autoCommit);\n    }\n\n    /**\n     * 执行 SQL 脚本文件\n     *\n     * @param sqlFiles   SQL 脚本文件列表\n     * @param autoCommit 自动提交事务\n     * @see DdlHelper#runScript(IDdlGenerator, DataSource, List, boolean)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public void run(List<String> sqlFiles, boolean autoCommit) {\n        try (Connection connection = this.dataSource.getConnection()) {\n            DdlHelper.runScript(this.ddlGenerator, connection, sqlFiles, this.scriptRunnerConsumer, autoCommit, DdlScriptErrorHandler.PrintlnLogErrorHandler.INSTANCE);\n        } catch (Exception e) {\n            // TODO 保持兼容,吞掉所有异常\n            LOG.error(\"Run script error: \", e);\n        }\n    }\n\n    /**\n     * 自定义 ScriptRunner\n     *\n     * @param scriptRunnerConsumer 处理函数\n     * @return this\n     * @since 3.5.11\n     */\n    public DdlScript scriptRunner(Consumer<ScriptRunner> scriptRunnerConsumer) {\n        this.scriptRunnerConsumer = scriptRunnerConsumer;\n        return this;\n    }\n\n    /**\n     * 运行 SQL 脚本\n     *\n     * @param sqlScript 脚本内容\n     * @throws Exception {@link org.apache.ibatis.jdbc.RuntimeSqlException}\n     */\n    public void run(String sqlScript) throws Exception {\n        this.run(sqlScript, StringPool.SEMICOLON);\n    }\n\n    /**\n     * 执行 SQL 脚本\n     *\n     * @param sqlScript SQL 脚本内容\n     * @param delimiter 执行 SQL 分隔符，默认 ; 符号结束执行\n     * @throws Exception {@link org.apache.ibatis.jdbc.RuntimeSqlException}\n     */\n    public void run(String sqlScript, String delimiter) throws Exception {\n        this.run(new StringReader(sqlScript), this.autoCommit, delimiter);\n    }\n\n    public void run(Reader reader) throws Exception {\n        this.run(reader, this.autoCommit, StringPool.SEMICOLON);\n    }\n\n    public void run(Reader reader, boolean autoCommit) throws Exception {\n        this.run(reader, autoCommit, StringPool.SEMICOLON);\n    }\n\n    public void run(Reader reader, boolean autoCommit, String delimiter) throws Exception {\n        try (Connection connection = this.dataSource.getConnection()) {\n            this.run(connection, reader, autoCommit, delimiter);\n        }\n    }\n\n    /**\n     * 执行 SQL 脚本\n     *\n     * @param connection {@link Connection} 调用方需要自行控制关闭\n     * @param reader     SQL 脚本内容\n     * @param autoCommit 自动提交事务\n     * @param delimiter  执行 SQL 分隔符，默认 ; 符号结束执行\n     */\n    public void run(Connection connection, Reader reader, boolean autoCommit, String delimiter) {\n        ScriptRunner scriptRunner = DdlHelper.getScriptRunner(connection, autoCommit);\n        if (scriptRunnerConsumer != null) {\n            scriptRunnerConsumer.accept(scriptRunner);\n        }\n        if (StringUtils.isNotBlank(delimiter)) {\n            scriptRunner.setDelimiter(delimiter);\n        }\n        scriptRunner.runScript(reader);\n    }\n\n    /**\n     * 以默认分隔符(;) 执行 SQL 脚本\n     *\n     * @param driverClassName   连接驱动名\n     * @param url               连接地址\n     * @param user              数据库用户名\n     * @param password          数据库密码\n     * @param sqlScript         执行 SQL 脚本\n     * @param exceptionConsumer 异常处理\n     * @see DdlScript(String, String, String, String)\n     * @see #run(String, Consumer)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public boolean execute(final String driverClassName, final String url, final String user, final String password,\n                           final String sqlScript, Consumer<String> exceptionConsumer) {\n        return this.execute(driverClassName, url, user, password, sqlScript, StringPool.SEMICOLON, exceptionConsumer);\n    }\n\n    /**\n     * 以默认分隔符(;) 执行 SQL 脚本\n     *\n     * @param sqlScript         执行 SQL脚本\n     * @param exceptionConsumer 异常处理\n     * @since 3.5.11\n     */\n    public boolean run(String sqlScript, Consumer<String> exceptionConsumer) {\n        return this.run(sqlScript, StringPool.SEMICOLON, exceptionConsumer);\n    }\n\n    /**\n     * 以指定分隔符 执行 SQL 脚本\n     *\n     * @param driverClassName   连接驱动名\n     * @param url               连接地址\n     * @param user              数据库用户名\n     * @param password          数据库密码\n     * @param sqlScript         执行 SQL 脚本\n     * @param delimiter         执行 SQL 分隔符，默认 ; 符号结束执行\n     * @param exceptionConsumer 异常处理\n     * @return 操作结果\n     * @deprecated 3.5.11  {@link #run(String, String, Consumer)}\n     */\n    @Deprecated\n    public boolean execute(final String driverClassName, final String url, final String user, final String password,\n                           final String sqlScript, String delimiter, Consumer<String> exceptionConsumer) {\n        //一般不需要显示加载,只有很旧很旧的驱动才需要\n        if (StringUtils.isNotBlank(driverClassName)) {\n            Class<?> driverClass = ClassUtils.toClassConfident(driverClassName);\n            if (LOG.isDebugEnabled()) {\n                LOG.debug(\"Load driver class: \" + driverClass.getName());\n            }\n        }\n        try (Connection connection = DriverManager.getConnection(url, user, password)) {\n            this.run(connection, new StringReader(sqlScript), this.autoCommit, delimiter);\n            return true;\n        } catch (Exception e) {\n            LOG.error(\"Execute sqlScript: \" + sqlScript + \" , fail:\", e);\n            exceptionConsumer.accept(e.getMessage());\n        }\n        return false;\n    }\n\n    /**\n     * 以指定分隔符 执行 SQL 脚本\n     *\n     * @param sqlScript         执行 SQL 脚本\n     * @param exceptionConsumer 异常处理\n     * @param delimiter         执行 SQL 分隔符，默认 ; 符号结束执行\n     * @return 操作结果\n     * @since 3.5.11\n     */\n    public boolean run(String sqlScript, String delimiter, Consumer<String> exceptionConsumer) {\n        try (Connection connection = dataSource.getConnection()) {\n            this.run(connection, new StringReader(sqlScript), this.autoCommit, delimiter);\n            return true;\n        } catch (Exception e) {\n            LOG.error(\"Execute sqlScript: \" + sqlScript + \" , fail:\", e);\n            exceptionConsumer.accept(e.getMessage());\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/DdlScriptErrorHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl;\n\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.sql.SQLException;\n\n\n/**\n * 错误处理器\n *\n * @author nieqiurong\n * @since 3.5.11\n */\n@FunctionalInterface\npublic interface DdlScriptErrorHandler {\n\n    /**\n     * 错误处理\n     *\n     * @param sqlFile   执行的sql文件\n     * @param exception 异常信息 {@link org.apache.ibatis.jdbc.RuntimeSqlException}\n     * @throws SQLException SQLException\n     */\n    void handle(String sqlFile, Exception exception) throws SQLException;\n\n    /**\n     * 打印错误日志 (继续执行)\n     */\n    class PrintlnLogErrorHandler implements DdlScriptErrorHandler {\n\n        public static final PrintlnLogErrorHandler INSTANCE = new PrintlnLogErrorHandler();\n\n        public static final Log log = LogFactory.getLog(PrintlnLogErrorHandler.class);\n\n        @Override\n        public void handle(String sqlFile, Exception throwable) {\n            log.error(\"run script sql:\" + sqlFile + \", error: \", throwable);\n        }\n    }\n\n    /**\n     * 抛出错误 (中断后续文件执行)\n     */\n    class ThrowsErrorHandler implements DdlScriptErrorHandler {\n\n        public static final ThrowsErrorHandler INSTANCE = new ThrowsErrorHandler();\n\n        @Override\n        public void handle(String sqlFile, Exception throwable) throws SQLException {\n            throw new SQLException(\"Execute \" + sqlFile + \" fail. \", throwable);\n        }\n\n    }\n\n}\n\n\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/IDdl.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl;\n\nimport com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;\n\nimport javax.sql.DataSource;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * DDL 处理器\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic interface IDdl {\n\n    /**\n     * 执行 SQL 脚本\n     *\n     * @param consumer 指定数据源执行\n     */\n    void runScript(Consumer<DataSource> consumer);\n\n    /**\n     * DDL 生成器\n     */\n    default IDdlGenerator getDdlGenerator() {\n        return null;\n    }\n\n    /**\n     * 执行 SQL 脚本\n     * <p>Resources.getResourceAsReader(\"db/test.sql\")</p>\n     *\n     * @return SQL 脚本文件列表\n     */\n    List<String> getSqlFiles();\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/IDdlGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl.history;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.function.Function;\n\n/**\n * DDL 生成器接口\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic interface IDdlGenerator {\n\n    /**\n     * 表是否存在\n     *\n     * @param databaseName    数据库名称\n     * @param executeFunction 执行判断函数\n     * @return exist or no\n     * @deprecated 3.5.13 {@link #existTable(Connection)}\n     */\n    @Deprecated\n    default boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {\n        return false;\n    }\n\n    /**\n     *\n     * 检查{@link #getDdlHistory()}表是否存在\n     * @since 3.5.13\n     * @param connection 数据库连接\n     * @return 是否存在\n     * @throws SQLException SQLException\n     */\n    default boolean existTable(Connection connection) throws SQLException {\n        DatabaseMetaData metaData = connection.getMetaData();\n        try (ResultSet resultSet = metaData.getTables(connection.getCatalog(), connection.getSchema(), getDdlHistory(), new String[]{\"TABLE\"})) {\n            return resultSet.next();\n        }\n    }\n\n    /**\n     * 返回 DDL_HISTORY 表名\n     *\n     * @return SQL\n     */\n    default String getDdlHistory() {\n        return \"ddl_history\";\n    }\n\n    /**\n     * ddl_history sql\n     *\n     * @return SQL\n     */\n    String createDdlHistory();\n\n    /**\n     * select ddl_history sql\n     *\n     * @param script Sql Script\n     * @param type   Execute Type\n     * @return SQL\n     */\n    default String selectDdlHistory(String script, String type) {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"SELECT version FROM \").append(getDdlHistory()).append(\" WHERE script='\").append(script);\n        sql.append(\"' AND type='\").append(type).append(\"'\");\n        return sql.toString();\n    }\n\n    /**\n     * insert ddl_history sql\n     *\n     * @param script  Sql Script\n     * @param type    Execute Type\n     * @param version Execute Version\n     * @return SQL\n     */\n    default String insertDdlHistory(String script, String type, String version) {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"INSERT INTO \").append(getDdlHistory()).append(\"(script,type,version) VALUES ('\");\n        sql.append(script).append(\"','\").append(type).append(\"','\").append(version).append(\"')\");\n        return sql.toString();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/MysqlDdlGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl.history;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.util.function.Function;\n\n/**\n * Mysql DDL 生成器\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic class MysqlDdlGenerator implements IDdlGenerator {\n\n    public static IDdlGenerator newInstance() {\n        return new MysqlDdlGenerator();\n    }\n\n    @Override\n    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"SELECT COUNT(1) AS NUM from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='\");\n        sql.append(getDdlHistory()).append(\"' AND TABLE_TYPE='BASE TABLE'\");\n        if (StringUtils.isNotBlank(databaseName)) {\n            sql.append(\" AND TABLE_SCHEMA='\").append(databaseName).append(\"'\");\n        }\n        return executeFunction.apply(sql.toString());\n    }\n\n    @Override\n    public String createDdlHistory() {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"CREATE TABLE IF NOT EXISTS `\").append(getDdlHistory()).append(\"` (\");\n        sql.append(\"`script` varchar(500) NOT NULL COMMENT '脚本',\");\n        sql.append(\"`type` varchar(30) NOT NULL COMMENT '类型',\");\n        sql.append(\"`version` varchar(30) NOT NULL COMMENT '版本',\");\n        sql.append(\"PRIMARY KEY (`script`)\");\n        sql.append(\") COMMENT = 'DDL 版本';\");\n        return sql.toString();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/OracleDdlGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl.history;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.function.Function;\n\n/**\n * Oracle DDL 生成器\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic class OracleDdlGenerator implements IDdlGenerator {\n\n    /**\n     * 默认使用当前用户模式\n     * @since 3.5.13\n     */\n    private String schema;\n\n    public OracleDdlGenerator() {\n    }\n\n    public OracleDdlGenerator(String schema) {\n        this.schema = schema;\n    }\n\n    /**\n     * 基于当前用户模式实例\n     * @return OracleDdlGenerator\n     */\n    public static IDdlGenerator newInstance() {\n        return new OracleDdlGenerator();\n    }\n\n    @Override\n    public boolean existTable(Connection connection) throws SQLException {\n        DatabaseMetaData metaData = connection.getMetaData();\n        String schema = StringUtils.isNotBlank(this.schema) ? this.schema : connection.getSchema();\n        String tableName = getDdlHistory();\n        int index = tableName.lastIndexOf(StringPool.DOT);\n        if (index > 0) {\n            tableName = tableName.substring(index + 1);\n        }\n        tableName = tableName.replace(StringPool.QUOTE, StringPool.EMPTY);\n        try (ResultSet resultSet = metaData.getTables(connection.getCatalog(), schema, tableName, new String[]{\"TABLE\"})) {\n            return resultSet.next();\n        }\n    }\n\n    @Override\n    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {\n        return executeFunction.apply(\"SELECT COUNT(1) AS NUM FROM user_tables WHERE table_name='\"\n            + getDdlHistory() + \"'\");\n    }\n\n    @Override\n    public String getDdlHistory() {\n        if (StringUtils.isNotBlank(schema)) {\n            return schema + \".DDL_HISTORY\";\n        }\n        return \"DDL_HISTORY\";\n    }\n\n    @Override\n    public String createDdlHistory() {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"CREATE TABLE \").append(getDdlHistory()).append(\"(\");\n        sql.append(\"script NVARCHAR2(500) NOT NULL,\");\n        sql.append(\"type NVARCHAR2(30) NOT NULL,\");\n        sql.append(\"version NVARCHAR2(30) NOT NULL\");\n        sql.append(\");\");\n        return sql.toString();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/PostgreDdlGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl.history;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.Setter;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.function.Function;\n\n\n/**\n * PostgreSQL DDL 生成器\n *\n * @author hubin\n * @since 2021-06-22\n */\npublic class PostgreDdlGenerator implements IDdlGenerator {\n\n    /**\n     * schema模式(默认:public)\n     * <p>为了兼容,默认使用public,但指定为null时,使用jdbc指定的schema</p>\n     * @since 3.5.13\n     */\n    @Setter\n    private String schema = \"public\";\n\n    /**\n     * @deprecated 3.5.13 {@link #newInstance}\n     */\n    @Deprecated\n    public PostgreDdlGenerator() {\n    }\n\n    /**\n     * 创建PostgreDdlGenerator实例\n     * @since 3.5.13\n     * @param schema schema (可为null,当为null时为自动识别数据库连接的schema)\n     */\n    public PostgreDdlGenerator(String schema) {\n        this.schema = schema;\n    }\n\n    /**\n     * 默认实例 (基于public模式)\n     * @return PostgreDdlGenerator\n     */\n    public static IDdlGenerator newInstance() {\n        return newInstanceWithSchema(\"public\");\n    }\n\n    /**\n     * 手动指定schema\n     * @param schema schema\n     * @since 3.5.13\n     *  @return PostgreDdlGenerator\n     */\n    public static IDdlGenerator newInstanceWithSchema(String schema) {\n        return new PostgreDdlGenerator(schema);\n    }\n\n    /**\n     * 基于数据库连接自动识别schema\n     * @since 3.5.13\n     * @return PostgreDdlGenerator\n     */\n    public static IDdlGenerator newInstanceWithAutoSchema() {\n        return new PostgreDdlGenerator(StringPool.EMPTY);\n    }\n\n\n    @Override\n    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"SELECT COUNT(1) AS NUM from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='ddl_history' AND TABLE_TYPE='BASE TABLE'\");\n        if (StringUtils.isNotBlank(this.getSchema())) {\n            sql.append(\" AND TABLE_SCHEMA='\").append(this.getSchema()).append(\"'\");\n        }\n        return executeFunction.apply(sql.toString());\n    }\n\n    @Override\n    public boolean existTable(Connection connection) throws SQLException {\n        DatabaseMetaData metaData = connection.getMetaData();\n        String tableName = getDdlHistory();\n        int index = tableName.lastIndexOf(StringPool.DOT);\n        if (index > 0) {\n            tableName = tableName.substring(index + 1);\n        }\n        tableName = tableName.replace(StringPool.QUOTE, StringPool.EMPTY);\n        try (ResultSet resultSet = metaData.getTables(connection.getCatalog(),\n            StringUtils.isBlank(getSchema()) ? connection.getSchema() : getSchema(), tableName, new String[]{\"TABLE\"})) {\n            return resultSet.next();\n        }\n    }\n\n    @Override\n    public String getDdlHistory() {\n        String schema = getSchema();\n        if (StringUtils.isNotBlank(schema)) {\n            return \"\\\"\" + schema + \"\\\".\\\"ddl_history\\\"\";\n        }\n        return \"\\\"ddl_history\\\"\";\n    }\n\n    @Override\n    public String createDdlHistory() {\n        StringBuffer sql = new StringBuffer();\n        String ddlHistory = this.getDdlHistory();\n        sql.append(\"CREATE TABLE IF NOT EXISTS \").append(ddlHistory).append(\" (\");\n        sql.append(\"\\\"script\\\" varchar(500) NOT NULL,\");\n        sql.append(\"\\\"type\\\" varchar(30) NOT NULL,\");\n        sql.append(\"\\\"version\\\" varchar(30) NOT NULL\");\n        sql.append(\");\");\n        sql.append(\"COMMENT ON COLUMN \").append(ddlHistory).append(\".\\\"script\\\" IS '脚本';\");\n        sql.append(\"COMMENT ON COLUMN \").append(ddlHistory).append(\".\\\"type\\\" IS '类型';\");\n        sql.append(\"COMMENT ON COLUMN \").append(ddlHistory).append(\".\\\"version\\\" IS '版本';\");\n        sql.append(\"COMMENT ON TABLE \").append(ddlHistory).append(\" IS 'DDL 版本';\");\n        return sql.toString();\n    }\n\n    /**\n     * @return scheme\n     * @deprecated 3.5.13 指定请使用 {@link #setSchema(String)}\n     */\n    @Deprecated\n    protected String getSchema() {\n        return schema;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/ddl/history/SQLiteDdlGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl.history;\n\nimport java.util.function.Function;\n\n/**\n * SQLite DDL 生成器\n *\n * @author 呆猫\n * @since 2024-04-01\n */\npublic class SQLiteDdlGenerator implements IDdlGenerator {\n\n    public static IDdlGenerator newInstance() {\n        return new SQLiteDdlGenerator();\n    }\n\n    @Override\n    public boolean existTable(String databaseName, Function<String, Boolean> executeFunction) {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"SELECT count(1) NUM FROM sqlite_master WHERE name='\");\n        sql.append(getDdlHistory()).append(\"' AND type='table'\");\n        return executeFunction.apply(sql.toString());\n    }\n\n    @Override\n    public String createDdlHistory() {\n        StringBuffer sql = new StringBuffer();\n        sql.append(\"CREATE TABLE IF NOT EXISTS `\").append(getDdlHistory()).append(\"` (\");\n        sql.append(\"script  TEXT primary key,\");\n        sql.append(\"type    TEXT,\");\n        sql.append(\"version TEXT\");\n        sql.append(\");\");\n        return sql.toString();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/AbstractJsonTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.type.BaseTypeHandler;\nimport org.apache.ibatis.type.JdbcType;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Type;\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * @author miemie\n * @since 2019-11-28\n */\npublic abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> implements IJsonTypeHandler<T> {\n\n    protected final Log log = LogFactory.getLog(this.getClass());\n\n    protected final Class<?> type;\n\n    /**\n     * @since 3.5.6\n     */\n    protected Type genericType;\n\n    /**\n     * 默认初始化\n     *\n     * @param type 类型\n     */\n    public AbstractJsonTypeHandler(Class<?> type) {\n        this.type = type;\n        if (log.isTraceEnabled()) {\n            log.trace(this.getClass().getSimpleName() + \"(\" + type + \")\");\n        }\n        Assert.notNull(type, \"Type argument cannot be null\");\n    }\n\n    /**\n     * 通过字段初始化\n     *\n     * @param type  类型\n     * @param field 字段\n     * @since 3.5.6\n     */\n    public AbstractJsonTypeHandler(Class<?> type, Field field) {\n        this(type);\n        this.genericType = field.getGenericType();\n    }\n\n    @Override\n    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {\n        ps.setString(i, toJson(parameter));\n    }\n\n    @Override\n    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {\n        final String json = rs.getString(columnName);\n        return StringUtils.isBlank(json) ? null : parse(json);\n    }\n\n    @Override\n    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {\n        final String json = rs.getString(columnIndex);\n        return StringUtils.isBlank(json) ? null : parse(json);\n    }\n\n    @Override\n    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {\n        final String json = cs.getString(columnIndex);\n        return StringUtils.isBlank(json) ? null : parse(json);\n    }\n\n    public Type getFieldType() {\n        return this.genericType != null ? this.genericType : this.type;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/Fastjson2TypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjson2.JSONWriter;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.lang.reflect.Field;\n\n/**\n * Fastjson2 实现 JSON 字段类型处理器\n *\n * @author nieqiurong\n * @since 3.5.5\n */\n@MappedTypes({Object.class})\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class Fastjson2TypeHandler extends AbstractJsonTypeHandler<Object> {\n\n    public Fastjson2TypeHandler(Class<?> type) {\n        super(type);\n    }\n\n    public Fastjson2TypeHandler(Class<?> type, Field field) {\n        super(type, field);\n    }\n\n    @Override\n    public Object parse(String json) {\n        return JSON.parseObject(json, this.getFieldType());\n    }\n\n    @Override\n    public String toJson(Object obj) {\n        return JSON.toJSONString(obj, JSONWriter.Feature.WriteMapNullValue,\n            JSONWriter.Feature.WriteNullListAsEmpty, JSONWriter.Feature.WriteNullStringAsEmpty);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/FastjsonTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.lang.reflect.Field;\n\n/**\n * Fastjson 实现 JSON 字段类型处理器\n *\n * @author hubin\n * @since 2019-08-25\n */\n@MappedTypes({Object.class})\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class FastjsonTypeHandler extends AbstractJsonTypeHandler<Object> {\n\n    public FastjsonTypeHandler(Class<?> type) {\n        super(type);\n    }\n\n    public FastjsonTypeHandler(Class<?> type, Field field) {\n        super(type, field);\n    }\n\n    @Override\n    public Object parse(String json) {\n        return JSON.parseObject(json, this.getFieldType());\n    }\n\n    @Override\n    public String toJson(Object obj) {\n        return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue,\n            SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/GsonTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.google.gson.Gson;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.lang.reflect.Field;\n\n/**\n * Gson 实现 JSON 字段类型处理器\n *\n * @author hubin\n * @since 2019-08-25\n */\n@MappedTypes({Object.class})\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class GsonTypeHandler extends AbstractJsonTypeHandler<Object> {\n\n    private static Gson GSON;\n\n    public GsonTypeHandler(Class<?> type) {\n        super(type);\n    }\n\n    public GsonTypeHandler(Class<?> type, Field field) {\n        super(type, field);\n    }\n\n    @Override\n    public Object parse(String json) {\n        return getGson().fromJson(json, this.getFieldType());\n    }\n\n    @Override\n    public String toJson(Object obj) {\n        return getGson().toJson(obj);\n    }\n\n    public static Gson getGson() {\n        return GSON == null ? Instance.GSON  : GSON;\n    }\n\n    public static void setGson(Gson gson) {\n        Assert.notNull(gson, \"Gson should not be null\");\n        GsonTypeHandler.GSON = gson;\n    }\n\n    /**\n     * @since 3.5.15\n     */\n    private static class Instance {\n\n         private static final Gson GSON = new Gson();\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/Jackson3TypeHandler.java",
    "content": "package com.baomidou.mybatisplus.extension.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport tools.jackson.databind.JavaType;\nimport tools.jackson.databind.ObjectMapper;\nimport tools.jackson.databind.type.TypeFactory;\n\n\nimport java.lang.reflect.Field;\n\n/**\n * jackson tools 实现的字段类型处理器\n *\n * @author milo\n * @since 3.5.15\n */\npublic class Jackson3TypeHandler extends AbstractJsonTypeHandler<Object> {\n\n    private static ObjectMapper OBJECT_MAPPER;\n\n    public Jackson3TypeHandler(Class<?> type) {\n        super(type);\n    }\n\n    public Jackson3TypeHandler(Class<?> type, Field field) {\n        super(type, field);\n    }\n\n    @Override\n    public Object parse(String json) {\n        ObjectMapper objectMapper = getObjectMapper();\n        TypeFactory typeFactory = objectMapper.getTypeFactory();\n        JavaType javaType = typeFactory.constructType(getFieldType());\n        return objectMapper.readValue(json, javaType);\n    }\n\n    @Override\n    public String toJson(Object obj) {\n        return getObjectMapper().writeValueAsString(obj);\n    }\n\n    public static ObjectMapper getObjectMapper() {\n        return OBJECT_MAPPER == null ? Instance.OBJECT_MAPPER: OBJECT_MAPPER;\n    }\n\n    public static void setObjectMapper(ObjectMapper objectMapper) {\n        Assert.notNull(objectMapper, \"ObjectMapper should not be null\");\n        Jackson3TypeHandler.OBJECT_MAPPER = objectMapper;\n    }\n\n    private static class Instance {\n\n        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/JacksonTypeHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.fasterxml.jackson.core.JacksonException;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.type.TypeFactory;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.MappedJdbcTypes;\nimport org.apache.ibatis.type.MappedTypes;\n\nimport java.lang.reflect.Field;\n\n/**\n * Jackson 实现 JSON 字段类型处理器\n *\n * @author hubin\n * @since 2019-08-25\n */\n@MappedTypes({Object.class})\n@MappedJdbcTypes(JdbcType.VARCHAR)\npublic class JacksonTypeHandler extends AbstractJsonTypeHandler<Object> {\n\n    private static ObjectMapper OBJECT_MAPPER;\n\n    public JacksonTypeHandler(Class<?> type) {\n        super(type);\n    }\n\n    public JacksonTypeHandler(Class<?> type, Field field) {\n        super(type, field);\n    }\n\n    @Override\n    public Object parse(String json) {\n        ObjectMapper objectMapper = getObjectMapper();\n        TypeFactory typeFactory = objectMapper.getTypeFactory();\n        JavaType javaType = typeFactory.constructType(getFieldType());\n        try {\n            return objectMapper.readValue(json, javaType);\n        } catch (JacksonException e) {\n            log.error(\"deserialize json: \" + json + \" to \" + javaType + \" error \", e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public String toJson(Object obj) {\n        try {\n            return getObjectMapper().writeValueAsString(obj);\n        } catch (JsonProcessingException e) {\n            log.error(\"serialize \" + obj + \" to json error \", e);\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static ObjectMapper getObjectMapper() {\n        return OBJECT_MAPPER == null ? Instance.OBJECT_MAPPER : OBJECT_MAPPER;\n    }\n\n    public static void setObjectMapper(ObjectMapper objectMapper) {\n        Assert.notNull(objectMapper, \"ObjectMapper should not be null\");\n        JacksonTypeHandler.OBJECT_MAPPER = objectMapper;\n    }\n\n    /**\n     * @since 3.5.15\n     */\n    private static class Instance {\n\n        private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/MybatisMapWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.wrapper.MapWrapper;\n\nimport java.util.Map;\n\n/**\n * 返回Map结果集，下划线转驼峰\n *\n * @author yuxiaobin\n * @since 2017/12/19\n */\npublic class MybatisMapWrapper extends MapWrapper {\n\n    public MybatisMapWrapper(MetaObject metaObject, Map<String, Object> map) {\n        super(metaObject, map);\n    }\n\n    @Override\n    public String findProperty(String name, boolean useCamelCaseMapping) {\n        if (useCamelCaseMapping && !StringUtils.isCamel(name)) {\n            return StringUtils.underlineToCamel(name);\n        }\n        return name;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 扩展相关处理器\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.handlers;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/DB2KeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * DB2 Sequence\n *\n * @author Caratacus\n * @since 2017-06-12\n */\npublic class DB2KeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"values nextval for \" + incrementerName;\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.DB2;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/DmKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * DM Sequence\n *\n * @author cdtjj\n * @since 2022-04-22\n */\npublic class DmKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"SELECT \" + incrementerName + \".NEXTVAL FROM DUAL\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.DM;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/FirebirdKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\npublic class FirebirdKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"SELECT next value for \" + incrementerName + \" from rdb$database\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.FIREBIRD;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/H2KeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * H2 Sequence\n *\n * @author Caratacus\n * @since 2017-06-12\n */\npublic class H2KeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"select nextval('\" + incrementerName + \"')\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.H2;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/KingbaseKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * Kingbase Sequence\n *\n * @author kingbase\n * @since 2019-10-17\n */\npublic class KingbaseKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"select nextval('\" + incrementerName + \"')\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.KINGBASE_ES;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/LealoneKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * Lealone Sequence\n *\n * @author zhoujin7\n * @since 2023-05-10\n */\npublic class LealoneKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"select nextval('\" + incrementerName + \"')\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.LEALONE;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/OracleKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * Oracle Key Sequence 生成器\n *\n * @author hubin\n * @since 2017-05-08\n */\npublic class OracleKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"SELECT \" + incrementerName + \".NEXTVAL FROM DUAL\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.ORACLE;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/PostgreKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * Postgres Sequence\n *\n * @author Caratacus\n * @since 2017-06-12\n */\npublic class PostgreKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"select nextval('\" + incrementerName + \"')\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.POSTGRE_SQL;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/SapHanaKeyGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\n\n/**\n * SAP_HANA Key Sequence 生成器\n *\n * @author zhangchg\n * @since 2021-12-03\n */\npublic class SapHanaKeyGenerator implements IKeyGenerator {\n\n    @Override\n    public String executeSql(String incrementerName) {\n        return \"SELECT \" + incrementerName + \".NEXTVAL FROM DUMMY\";\n    }\n\n    @Override\n    public DbType dbType() {\n        return DbType.SAP_HANA;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/incrementer/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 扩展主键自增生成器\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.incrementer;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/AlwaysUpdateSomeColumnById.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport lombok.Setter;\nimport lombok.experimental.Accessors;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.function.Predicate;\n\n/**\n * 根据 ID 更新固定的那几个字段(但是不包含逻辑删除)\n *\n * <p>\n * 自己的通用 mapper 如下使用:\n * <pre>\n * int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);\n * </pre>\n * </p>\n *\n * <p> 如何筛选字段参考请 {@link InsertBatchSomeColumn} 里面的注释 </p>\n *\n * @author hubin\n * @since 2019-04-12\n */\npublic class AlwaysUpdateSomeColumnById extends AbstractMethod {\n\n    /**\n     * 字段筛选条件\n     */\n    @Setter\n    @Accessors(chain = true)\n    private Predicate<TableFieldInfo> predicate;\n\n    /**\n     * @param name      方法名\n     * @param predicate 筛选条件\n     * @since 3.5.0\n     */\n    public AlwaysUpdateSomeColumnById(String name, Predicate<TableFieldInfo> predicate) {\n        super(name);\n        this.predicate = predicate;\n    }\n\n    public AlwaysUpdateSomeColumnById() {\n        super(\"alwaysUpdateSomeColumnById\");\n    }\n\n    /**\n     * @param predicate 筛选条件\n     */\n    public AlwaysUpdateSomeColumnById(Predicate<TableFieldInfo> predicate) {\n        super(\"alwaysUpdateSomeColumnById\");\n        this.predicate = predicate;\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;\n        final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);\n        String sqlSet = this.filterTableFieldInfo(tableInfo.getFieldList(), getPredicate(),\n            i -> i.getSqlSet(true, ENTITY_DOT), NEWLINE);\n        sqlSet = SqlScriptUtils.convertSet(sqlSet);\n        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet,\n            tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional);\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n    }\n\n    private Predicate<TableFieldInfo> getPredicate() {\n        Predicate<TableFieldInfo> noLogic = t -> !t.isLogicDelete();\n        if (predicate != null) {\n            return noLogic.and(predicate);\n        }\n        return noLogic;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/InsertBatchSomeColumn.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport lombok.Setter;\nimport lombok.experimental.Accessors;\nimport org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * 批量新增数据,自选字段 insert\n * <p> 不同的数据库支持度不一样!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!! </p>\n * <p> 除了主键是 <strong> 数据库自增的未测试 </strong> 外理论上都可以使用!!! </p>\n * <p> 如果你使用自增有报错或主键值无法回写到entity,就不要跑来问为什么了,因为我也不知道!!! </p>\n * <p>\n * 自己的通用 mapper 如下使用:\n * <pre>\n * int insertBatchSomeColumn(List<T> entityList);\n * </pre>\n * </p>\n *\n * <li> 注意: 这是自选字段 insert !!,如果个别字段在 entity 里为 null 但是数据库中有配置默认值, insert 后数据库字段是为 null 而不是默认值 </li>\n *\n * <p>\n * 常用的 {@link Predicate}:\n * </p>\n *\n * <li> 例1: t -> !t.isLogicDelete() , 表示不要逻辑删除字段 </li>\n * <li> 例2: t -> !t.getProperty().equals(\"version\") , 表示不要字段名为 version 的字段 </li>\n * <li> 例3: t -> t.getFieldFill() != FieldFill.UPDATE) , 表示不要填充策略为 UPDATE 的字段 </li>\n *\n * @author miemie\n * @since 2018-11-29\n */\npublic class InsertBatchSomeColumn extends AbstractMethod {\n\n    /**\n     * 字段筛选条件\n     */\n    @Setter\n    @Accessors(chain = true)\n    private Predicate<TableFieldInfo> predicate;\n\n    /**\n     * 默认方法名\n     */\n    public InsertBatchSomeColumn() {\n        super(\"insertBatchSomeColumn\");\n    }\n\n    /**\n     * 默认方法名\n     *\n     * @param predicate 字段筛选条件\n     */\n    public InsertBatchSomeColumn(Predicate<TableFieldInfo> predicate) {\n        super(\"insertBatchSomeColumn\");\n        this.predicate = predicate;\n    }\n\n    /**\n     * @param name      方法名\n     * @param predicate 字段筛选条件\n     * @since 3.5.0\n     */\n    public InsertBatchSomeColumn(String name, Predicate<TableFieldInfo> predicate) {\n        super(name);\n        this.predicate = predicate;\n    }\n\n    @SuppressWarnings(\"Duplicates\")\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;\n        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;\n        List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true, null, false) +\n            this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);\n        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;\n        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) +\n            this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);\n        insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;\n        String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, \"list\", null, ENTITY, COMMA);\n        String keyProperty = null;\n        String keyColumn = null;\n        // 表包含主键处理逻辑,如果不包含主键当普通字段处理\n        if (tableInfo.havePK()) {\n            if (tableInfo.getIdType() == IdType.AUTO) {\n                /* 自增主键 */\n                keyGenerator = Jdbc3KeyGenerator.INSTANCE;\n                keyProperty = tableInfo.getKeyProperty();\n                // 去除转义符\n                keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());\n            } else {\n                if (null != tableInfo.getKeySequence()) {\n                    keyGenerator = TableInfoHelper.genKeyGenerator(this.methodName, tableInfo, builderAssistant);\n                    keyProperty = tableInfo.getKeyProperty();\n                    keyColumn = tableInfo.getKeyColumn();\n                }\n            }\n        }\n        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/LogicDeleteBatchByIds.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.methods.DeleteByIds;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\n\nimport java.util.List;\n\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toList;\n\n/**\n * 试验功能,不做太复杂的功能,逻辑删除增加填充功能\n * 如果想做的通用点的话,delete的时候如果是主键,在service层转换实体进行删除,这样根据主键删除的也能自动填充\n * 如果是逻辑删除且标记有填充字段的情况下,以第一条记录的填充字段为准(一切以当前的时间点为基准,如果无法接受记录记录时间不准确请使用循环删除)\n * 由于本身SQL更新的限制限,这里记录集合不能为空,也不支持第一条记录删除人是A或者时间是A时间,第二条记录的时间是B时间\n * update table set (填充字段1,填充字段2,逻辑删除字段) where id in (主键1,主键2,主键3)\n * 用法:\n * <pre>\n *     使用默认deleteBatchIds方法\n *     注入方法: new LogicDeleteBatchByIds()\n * </pre>\n * <pre>\n * 自定义Mapper方法名:\n * 注入方法: new LogicDeleteBatchByIds(\"testDeleteBatch\")\n * 增加Mapper方法: int testDeleteBatch(@Param(Constants.COLLECTION) List<Entity> entityList);\n * </pre>\n *\n * @author nieqiurong\n * @since 3.5.0\n * @deprecated 3.5.7 {@link DeleteByIds}\n */\n@Deprecated\npublic class LogicDeleteBatchByIds extends DeleteByIds {\n\n    public LogicDeleteBatchByIds() {\n        super();\n    }\n\n    public LogicDeleteBatchByIds(String name) {\n        super(name);\n    }\n\n    @Override\n    public String logicDeleteScript(TableInfo tableInfo, SqlMethod sqlMethod) {\n        List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()\n            .filter(TableFieldInfo::isWithUpdateFill)\n            .filter(f -> !f.isLogicDelete())\n            .collect(toList());\n        if (CollectionUtils.isNotEmpty(fieldInfos)) {\n            String sqlScript = fieldInfos.stream()\n                .map(i -> i.getSqlSet(COLL + \"[0].\")).collect(joining(EMPTY));\n            String sqlSet = \"SET \" + SqlScriptUtils.convertIf(sqlScript, \"!@org.apache.ibatis.type.SimpleTypeRegistry@isSimpleType(_parameter.getClass())\", true)\n                + tableInfo.getLogicDeleteSql(false, false);\n            return String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),\n                SqlScriptUtils.convertForeach(\n                    SqlScriptUtils.convertChoose(\"@org.apache.ibatis.type.SimpleTypeRegistry@isSimpleType(item.getClass())\",\n                        \"#{item}\", \"#{item.\" + tableInfo.getKeyProperty() + \"}\"),\n                    COLL, null, \"item\", COMMA),\n                tableInfo.getLogicDeleteSql(true, true));\n        } else {\n            return super.logicDeleteScript(tableInfo, sqlMethod);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/LogicDeleteByIdWithFill.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\nimport java.util.List;\n\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toList;\n\n/**\n * 根据 id 逻辑删除数据,并带字段填充功能\n * <p>注意入参是 entity !!! ,如果字段没有自动填充,就只是单纯的逻辑删除</p>\n * <p>\n * 自己的通用 mapper 如下使用:\n * <pre>\n * int deleteByIdWithFill(T entity);\n * </pre>\n * </p>\n *\n * @author miemie\n * @since 2018-11-09\n * @deprecated 3.5.0 {@link com.baomidou.mybatisplus.core.injector.methods.DeleteById}\n */\n@Deprecated\npublic class LogicDeleteByIdWithFill extends AbstractMethod {\n\n    public LogicDeleteByIdWithFill() {\n        super(\"deleteByIdWithFill\");\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public LogicDeleteByIdWithFill(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        String sql;\n        SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;\n        if (tableInfo.isWithLogicDelete()) {\n            List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()\n                .filter(TableFieldInfo::isWithUpdateFill)\n                .filter(f -> !f.isLogicDelete())\n                .collect(toList());\n            if (CollectionUtils.isNotEmpty(fieldInfos)) {\n                String sqlSet = \"SET \" + fieldInfos.stream().map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY))\n                    + tableInfo.getLogicDeleteSql(false, false);\n                sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),\n                    tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true));\n            } else {\n                sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),\n                    tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),\n                    tableInfo.getLogicDeleteSql(true, true));\n            }\n        } else {\n            sqlMethod = SqlMethod.DELETE_BY_ID;\n            sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),\n                tableInfo.getKeyProperty());\n        }\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/Upsert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.injector.AbstractMethod;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;\nimport org.apache.ibatis.executor.keygen.KeyGenerator;\nimport org.apache.ibatis.executor.keygen.NoKeyGenerator;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlSource;\n\n/**\n * 插入一条数据（选择字段插入）\n *\n * @author fly\n * @since 2018-04-06\n */\npublic class Upsert extends AbstractMethod {\n\n    public Upsert() {\n        super(SqlMethod.UPSERT_ONE.getMethod());\n    }\n\n    /**\n     * @param name 方法名\n     * @since 3.5.0\n     */\n    public Upsert(String name) {\n        super(name);\n    }\n\n    @Override\n    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {\n        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;\n        SqlMethod sqlMethod = SqlMethod.UPSERT_ONE;\n        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),\n            LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);\n        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),\n            LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);\n        String keyProperty = null;\n        String keyColumn = null;\n        // 表包含主键处理逻辑,如果不包含主键当普通字段处理\n        if (tableInfo.havePK()) {\n            // 自增主键会造成HBase单Region数据挤压，直接移除\n            if (null != tableInfo.getKeySequence()) {\n                keyGenerator = TableInfoHelper.genKeyGenerator(this.methodName, tableInfo, builderAssistant);\n                keyProperty = tableInfo.getKeyProperty();\n                keyColumn = tableInfo.getKeyColumn();\n            }\n        }\n        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);\n        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);\n        return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/methods/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 扩展注入 SQL 操作方法相关类\n *\n * @author hubin\n * @since 2018-06-13\n */\npackage com.baomidou.mybatisplus.extension.injector.methods;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/injector/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 扩展注入核心代码\n *\n * @author hubin\n * @since 2018-06-13\n */\npackage com.baomidou.mybatisplus.extension.injector;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/p6spy/MybatisPlusLogFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.p6spy;\n\nimport com.p6spy.engine.event.JdbcEventListener;\nimport com.p6spy.engine.logging.P6LogOptions;\nimport com.p6spy.engine.spy.P6Factory;\nimport com.p6spy.engine.spy.P6LoadableOptions;\nimport com.p6spy.engine.spy.option.P6OptionsRepository;\n\n/**\n * 扩展 p6spy\n *\n * @author nieqiurong\n * @since 2019-11-10\n */\npublic class MybatisPlusLogFactory implements P6Factory {\n\n    @Override\n    public P6LoadableOptions getOptions(P6OptionsRepository optionsRepository) {\n        return new P6LogOptions(optionsRepository);\n    }\n\n    @Override\n    public JdbcEventListener getJdbcEventListener() {\n        return MybatisPlusLoggingEventListener.getInstance();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/p6spy/MybatisPlusLoggingEventListener.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.p6spy;\n\nimport com.p6spy.engine.common.StatementInformation;\nimport com.p6spy.engine.logging.LoggingEventListener;\n\nimport java.sql.SQLException;\n\n/**\n * 监听事件\n *\n * @author nieqiurong\n * @since 2019-11-10\n */\npublic class MybatisPlusLoggingEventListener extends LoggingEventListener {\n    private static MybatisPlusLoggingEventListener INSTANCE;\n\n    public static MybatisPlusLoggingEventListener getInstance() {\n        if (null == INSTANCE) {\n            INSTANCE = new MybatisPlusLoggingEventListener();\n        }\n        return INSTANCE;\n    }\n\n    @Override\n    public void onAfterExecuteBatch(StatementInformation statementInformation, long timeElapsedNanos, int[] updateCounts, SQLException e) {\n        //忽略批量执行结果\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/p6spy/P6SpyLogger.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.p6spy;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.p6spy.engine.spy.appender.MessageFormattingStrategy;\n\n/**\n * P6spy SQL 打印策略\n *\n * @author hubin\n * @since 2019-02-20\n */\npublic class P6SpyLogger implements MessageFormattingStrategy {\n\n    @Override\n    public String formatMessage(int connectionId, String now, long elapsed, String category,\n                                String prepared, String sql, String url) {\n        return StringUtils.isNotBlank(sql) ? \" Consume Time：\" + elapsed + \" ms \" + now +\n            \"\\n Execute SQL：\" + sql.replaceAll(\"[\\\\s]+\", \" \") + \"\\n\" : \"\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/p6spy/StdoutLogger.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.p6spy;\n\n/**\n * 输出 SQL 日志\n *\n * @author hubin\n * @since 2019-02-20\n */\npublic class StdoutLogger extends com.p6spy.engine.spy.appender.StdoutLogger {\n\n    @Override\n    public void logText(String text) {\n        // 打印红色 SQL 日志\n        System.err.println(text);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * mybatis-plus扩展功能，包括分页，sql解析，spring集成\n *\n * @author yuxiaobin\n * @since 2018/2/6\n */\npackage com.baomidou.mybatisplus.extension;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/MybatisPlusInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins;\n\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport lombok.Setter;\nimport org.apache.ibatis.cache.CacheKey;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.plugin.*;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.util.*;\n\n/**\n * @author miemie\n * @since 3.4.0\n */\n@SuppressWarnings({\"rawtypes\"})\n@Intercepts(\n    {\n        @Signature(type = StatementHandler.class, method = \"prepare\", args = {Connection.class, Integer.class}),\n        @Signature(type = StatementHandler.class, method = \"getBoundSql\", args = {}),\n        @Signature(type = Executor.class, method = \"update\", args = {MappedStatement.class, Object.class}),\n        @Signature(type = Executor.class, method = \"query\", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),\n        @Signature(type = Executor.class, method = \"query\", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),\n    }\n)\npublic class MybatisPlusInterceptor implements Interceptor {\n\n    @Setter\n    private List<InnerInterceptor> interceptors = new ArrayList<>();\n\n    @Override\n    public Object intercept(Invocation invocation) throws Throwable {\n        Object target = invocation.getTarget();\n        Object[] args = invocation.getArgs();\n        if (target instanceof Executor) {\n            final Executor executor = (Executor) target;\n            Object parameter = args[1];\n            boolean isUpdate = args.length == 2;\n            MappedStatement ms = (MappedStatement) args[0];\n            if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {\n                RowBounds rowBounds = (RowBounds) args[2];\n                ResultHandler resultHandler = (ResultHandler) args[3];\n                BoundSql boundSql;\n                if (args.length == 4) {\n                    boundSql = ms.getBoundSql(parameter);\n                } else {\n                    // 几乎不可能走进这里面,除非使用Executor的代理对象调用query[args[6]]\n                    boundSql = (BoundSql) args[5];\n                }\n                for (InnerInterceptor query : interceptors) {\n                    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {\n                        return Collections.emptyList();\n                    }\n                    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);\n                }\n                CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);\n                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);\n            } else if (isUpdate) {\n                for (InnerInterceptor update : interceptors) {\n                    if (!update.willDoUpdate(executor, ms, parameter)) {\n                        return -1;\n                    }\n                    update.beforeUpdate(executor, ms, parameter);\n                }\n            }\n        } else {\n            // StatementHandler\n            final StatementHandler sh = (StatementHandler) target;\n            // 目前只有StatementHandler.getBoundSql方法args才为null\n            if (null == args) {\n                for (InnerInterceptor innerInterceptor : interceptors) {\n                    innerInterceptor.beforeGetBoundSql(sh);\n                }\n            } else {\n                Connection connections = (Connection) args[0];\n                Integer transactionTimeout = (Integer) args[1];\n                for (InnerInterceptor innerInterceptor : interceptors) {\n                    innerInterceptor.beforePrepare(sh, connections, transactionTimeout);\n                }\n            }\n        }\n        return invocation.proceed();\n    }\n\n    @Override\n    public Object plugin(Object target) {\n        if (target instanceof Executor || target instanceof StatementHandler) {\n            return Plugin.wrap(target, this);\n        }\n        return target;\n    }\n\n    public void addInnerInterceptor(InnerInterceptor innerInterceptor) {\n        this.interceptors.add(innerInterceptor);\n    }\n\n    public List<InnerInterceptor> getInterceptors() {\n        return Collections.unmodifiableList(interceptors);\n    }\n\n    /**\n     * 使用内部规则,拿分页插件举个栗子:\n     * <p>\n     * - key: \"@page\" ,value: \"com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor\"\n     * - key: \"page:limit\" ,value: \"100\"\n     * <p>\n     * 解读1: key 以 \"@\" 开头定义了这是一个需要组装的 `InnerInterceptor`, 以 \"page\" 结尾表示别名\n     * value 是 `InnerInterceptor` 的具体的 class 全名\n     * 解读2: key 以上面定义的 \"别名 + ':'\" 开头指这个 `value` 是定义的该 `InnerInterceptor` 属性需要设置的值\n     * <p>\n     * 如果这个 `InnerInterceptor` 不需要配置属性也要加别名\n     */\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper pm = PropertyMapper.newInstance(properties);\n        Map<String, Properties> group = pm.group(StringPool.AT);\n        group.forEach((k, v) -> {\n            InnerInterceptor innerInterceptor = ClassUtils.newInstance(k);\n            innerInterceptor.setProperties(v);\n            addInnerInterceptor(innerInterceptor);\n        });\n    }\n\n    @Override\n    public String toString() {\n        return \"MybatisPlusInterceptor{\" +\n            \"interceptors=\" + interceptors +\n            '}';\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TableNameHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\n/**\n * 动态表名处理器\n *\n * @author miemie\n * @since 3.4.0\n */\npublic interface TableNameHandler {\n\n    /**\n     * 生成动态表名\n     *\n     * @param sql       当前执行 SQL\n     * @param tableName 表名\n     * @return String\n     */\n    String dynamicTableName(String sql, String tableName);\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DynamicTableNameInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.TableNameParser;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 动态表名\n *\n * @author jobob\n * @since 3.4.0\n */\n@Getter\n@Setter\n@SuppressWarnings({\"rawtypes\"})\npublic class DynamicTableNameInnerInterceptor implements InnerInterceptor {\n\n    /**\n     * 回调处理\n     */\n    private Runnable hook;\n\n    /**\n     * 表名处理器，是否 处理表名的情况都在该处理器中自行判断\n     */\n    private TableNameHandler tableNameHandler = (sql, tableName) -> sql;\n\n    /**\n     * 默认构建\n     *\n     * @see #DynamicTableNameInnerInterceptor(TableNameHandler)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public DynamicTableNameInnerInterceptor() {\n    }\n\n    public DynamicTableNameInnerInterceptor(TableNameHandler tableNameHandler) {\n        this.tableNameHandler = tableNameHandler;\n    }\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) return;\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(this.changeTable(mpBs.sql()));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(this.changeTable(mpBs.sql()));\n        }\n    }\n\n    public String changeTable(String sql) {\n        try {\n            return processTableName(sql);\n        } finally {\n            if (hook != null) {\n                hook.run();\n            }\n        }\n    }\n\n    /**\n     * 处理表名解析替换\n     *\n     * @param sql 原始sql\n     * @return 处理完的sql\n     * @since 3.5.11\n     */\n    protected String processTableName(String sql) {\n        TableNameParser parser = new TableNameParser(sql);\n        List<TableNameParser.SqlToken> names = new ArrayList<>();\n        parser.accept(names::add);\n        StringBuilder builder = new StringBuilder();\n        int last = 0;\n        for (TableNameParser.SqlToken name : names) {\n            int start = name.getStart();\n            if (start != last) {\n                builder.append(sql, last, start);\n                builder.append(tableNameHandler.dynamicTableName(sql, name.getValue()));\n            }\n            last = name.getEnd();\n        }\n        if (last != sql.length()) {\n            builder.append(sql.substring(last));\n        }\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/InnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport org.apache.ibatis.cache.CacheKey;\nimport org.apache.ibatis.executor.BatchExecutor;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.ReuseExecutor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.Properties;\n\n/**\n * @author miemie\n * @since 3.4.0\n */\n@SuppressWarnings({\"rawtypes\"})\npublic interface InnerInterceptor {\n\n    /**\n     * 判断是否执行 {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)}\n     * <p>\n     * 如果不执行query操作,则返回 {@link Collections#emptyList()}\n     *\n     * @param executor      Executor(可能是代理对象)\n     * @param ms            MappedStatement\n     * @param parameter     parameter\n     * @param rowBounds     rowBounds\n     * @param resultHandler resultHandler\n     * @param boundSql      boundSql\n     * @return 新的 boundSql\n     */\n    default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        return true;\n    }\n\n    /**\n     * {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)} 操作前置处理\n     * <p>\n     * 改改sql啥的\n     *\n     * @param executor      Executor(可能是代理对象)\n     * @param ms            MappedStatement\n     * @param parameter     parameter\n     * @param rowBounds     rowBounds\n     * @param resultHandler resultHandler\n     * @param boundSql      boundSql\n     */\n    default void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        // do nothing\n    }\n\n    /**\n     * 判断是否执行 {@link Executor#update(MappedStatement, Object)}\n     * <p>\n     * 如果不执行update操作,则影响行数的值为 -1\n     *\n     * @param executor  Executor(可能是代理对象)\n     * @param ms        MappedStatement\n     * @param parameter parameter\n     */\n    default boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {\n        return true;\n    }\n\n    /**\n     * {@link Executor#update(MappedStatement, Object)} 操作前置处理\n     * <p>\n     * 改改sql啥的\n     *\n     * @param executor  Executor(可能是代理对象)\n     * @param ms        MappedStatement\n     * @param parameter parameter\n     */\n    default void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {\n        // do nothing\n    }\n\n    /**\n     * {@link StatementHandler#prepare(Connection, Integer)} 操作前置处理\n     * <p>\n     * 改改sql啥的\n     *\n     * @param sh                 StatementHandler(可能是代理对象)\n     * @param connection         Connection\n     * @param transactionTimeout transactionTimeout\n     */\n    default void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        // do nothing\n    }\n\n    /**\n     * {@link StatementHandler#getBoundSql()} 操作前置处理\n     * <p>\n     * 只有 {@link BatchExecutor} 和 {@link ReuseExecutor} 才会调用到这个方法\n     *\n     * @param sh StatementHandler(可能是代理对象)\n     */\n    default void beforeGetBoundSql(StatementHandler sh) {\n        // do nothing\n    }\n\n    default void setProperties(Properties properties) {\n        // do nothing\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/OptimisticLockerInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.annotation.Version;\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper;\nimport com.baomidou.mybatisplus.core.conditions.ISqlSegment;\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;\nimport com.baomidou.mybatisplus.core.conditions.update.Update;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\nimport com.baomidou.mybatisplus.core.mapper.Mapper;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport lombok.Setter;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.lang.reflect.Field;\nimport java.sql.Timestamp;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Optimistic Lock Light version\n * <p>Intercept on {@link Executor}.update;</p>\n * <p>Support version types: int/Integer, long/Long, java.util.Date, java.sql.Timestamp</p>\n * <p>For extra types, please define a subclass and override {@code getUpdatedVersionVal}() method.</p>\n * <br>\n * <p>How to use?</p>\n * <p>(1) Define an Entity and add {@link Version} annotation on one entity field.</p>\n * <p>(2) Add {@link OptimisticLockerInnerInterceptor} into mybatis plugin.</p>\n * <br>\n * <p>How to work?</p>\n * <p>if update entity with version column=1:</p>\n * <p>(1) no {@link OptimisticLockerInnerInterceptor}:</p>\n * <p>SQL: update tbl_test set name='abc' where id=100001;</p>\n * <p>(2) add {@link OptimisticLockerInnerInterceptor}:</p>\n * <p>SQL: update tbl_test set name='abc',version=2 where id=100001 and version=1;</p>\n *\n * @author yuxiaobin\n * @since 3.4.0\n */\n@SuppressWarnings({\"unchecked\"})\npublic class OptimisticLockerInnerInterceptor implements InnerInterceptor {\n\n    /**\n     * 自定义异常处理\n     */\n    @Setter\n    private RuntimeException exception;\n\n    /**\n     * entity类缓存\n     */\n    private static final Map<String, Class<?>> ENTITY_CLASS_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 变量占位符正则\n     */\n    private static final Pattern PARAM_PAIRS_RE = Pattern.compile(\"#\\\\{ew\\\\.paramNameValuePairs\\\\.(\" + Constants.WRAPPER_PARAM + \"\\\\d+)\\\\}\");\n\n    /**\n     * paramNameValuePairs存放的version值的key\n     */\n    private static final String UPDATED_VERSION_VAL_KEY = \"#updatedVersionVal#\";\n\n    /**\n     * Support wrapper mode (update(LambdaUpdateWrapper) or update(UpdateWrapper))\n     */\n    private final boolean wrapperMode;\n\n    public OptimisticLockerInnerInterceptor() {\n        this(false);\n    }\n\n    public OptimisticLockerInnerInterceptor(boolean wrapperMode) {\n        this.wrapperMode = wrapperMode;\n    }\n\n    @Override\n    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {\n        if (SqlCommandType.UPDATE != ms.getSqlCommandType()) {\n            return;\n        }\n        if (parameter instanceof Map) {\n            Map<String, Object> map = (Map<String, Object>) parameter;\n            doOptimisticLocker(map, ms.getId());\n        }\n    }\n\n    protected void doOptimisticLocker(Map<String, Object> map, String msId) {\n        // updateById(et), update(et, wrapper);\n        Object et = map.getOrDefault(Constants.ENTITY, null);\n        if (Objects.nonNull(et)) {\n\n            // version field\n            TableFieldInfo fieldInfo = this.getVersionFieldInfo(et.getClass());\n            if (null == fieldInfo) {\n                return;\n            }\n\n            try {\n                Field versionField = fieldInfo.getField();\n                // 旧的 version 值\n                Object originalVersionVal = versionField.get(et);\n                if (originalVersionVal == null) {\n                    if (null != exception) {\n                        throw exception;\n                    }\n                    return;\n                }\n                String versionColumn = fieldInfo.getColumn();\n                // 新的 version 值\n                Object updatedVersionVal = this.getUpdatedVersionVal(fieldInfo.getPropertyType(), originalVersionVal);\n                String methodName = msId.substring(msId.lastIndexOf(StringPool.DOT) + 1);\n                if (\"update\".equals(methodName)) {\n                    AbstractWrapper<?, ?, ?> aw = (AbstractWrapper<?, ?, ?>) map.getOrDefault(Constants.WRAPPER, null);\n                    if (aw == null) {\n                        UpdateWrapper<?> uw = new UpdateWrapper<>();\n                        uw.eq(versionColumn, originalVersionVal);\n                        map.put(Constants.WRAPPER, uw);\n                    } else {\n                        aw.apply(versionColumn + \" = {0}\", originalVersionVal);\n                    }\n                } else {\n                    map.put(Constants.MP_OPTLOCK_VERSION_ORIGINAL, originalVersionVal);\n                }\n                versionField.set(et, updatedVersionVal);\n            } catch (IllegalAccessException e) {\n                throw ExceptionUtils.mpe(e);\n            }\n        } else if (wrapperMode && map.containsKey(Constants.WRAPPER)) {\n            setVersionByWrapper(map, msId);\n        }\n    }\n\n    protected TableFieldInfo getVersionFieldInfo(Class<?> entityClazz) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClazz);\n        return (null != tableInfo && tableInfo.isWithVersion()) ? tableInfo.getVersionFieldInfo() : null;\n    }\n\n    private void setVersionByWrapper(Map<String, Object> map, String msId) {\n        Object ew = map.get(Constants.WRAPPER);\n        if (ew instanceof AbstractWrapper && ew instanceof Update) {\n            Class<?> entityClass = ENTITY_CLASS_CACHE.get(msId);\n            if (null == entityClass) {\n                try {\n                    final String className = msId.substring(0, msId.lastIndexOf('.'));\n                    entityClass = ReflectionKit.getSuperClassGenericType(Class.forName(className), Mapper.class, 0);\n                    ENTITY_CLASS_CACHE.put(msId, entityClass);\n                } catch (ClassNotFoundException e) {\n                    throw ExceptionUtils.mpe(e);\n                }\n            }\n\n            final TableFieldInfo versionField = getVersionFieldInfo(entityClass);\n            if (null == versionField) {\n                return;\n            }\n\n            final String versionColumn = versionField.getColumn();\n            final FieldEqFinder fieldEqFinder = new FieldEqFinder(versionColumn, (Wrapper<?>) ew);\n            if (!fieldEqFinder.isPresent()) {\n                return;\n            }\n            final Map<String, Object> paramNameValuePairs = ((AbstractWrapper<?, ?, ?>) ew).getParamNameValuePairs();\n            final Object originalVersionValue = paramNameValuePairs.get(fieldEqFinder.valueKey);\n            if (originalVersionValue == null) {\n                return;\n            }\n            final Object updatedVersionVal = getUpdatedVersionVal(originalVersionValue.getClass(), originalVersionValue);\n            if (originalVersionValue == updatedVersionVal) {\n                return;\n            }\n            // 拼接新的version值\n            paramNameValuePairs.put(UPDATED_VERSION_VAL_KEY, updatedVersionVal);\n            ((Update<?, ?>) ew).setSql(String.format(\"%s = #{%s.%s}\", versionColumn, \"ew.paramNameValuePairs\", UPDATED_VERSION_VAL_KEY));\n        }\n    }\n\n    /**\n     * EQ字段查找器\n     */\n    private static class FieldEqFinder {\n\n        /**\n         * 状态机\n         */\n        enum State {\n            INIT,\n            FIELD_FOUND,\n            EQ_FOUND,\n            VERSION_VALUE_PRESENT\n\n        }\n\n        /**\n         * 字段值的key\n         */\n        private String valueKey;\n        /**\n         * 当前状态\n         */\n        private State state;\n        /**\n         * 字段名\n         */\n        private final String fieldName;\n\n        public FieldEqFinder(String fieldName, Wrapper<?> wrapper) {\n            this.fieldName = fieldName;\n            state = State.INIT;\n            find(wrapper);\n        }\n\n        /**\n         * 是否已存在\n         */\n        public boolean isPresent() {\n            return state == State.VERSION_VALUE_PRESENT;\n        }\n\n        private boolean find(Wrapper<?> wrapper) {\n            Matcher matcher;\n            final NormalSegmentList segments = wrapper.getExpression().getNormal();\n            for (ISqlSegment segment : segments) {\n                // 如果字段已找到并且当前segment为EQ\n                if (state == State.FIELD_FOUND && segment == SqlKeyword.EQ) {\n                    this.state = State.EQ_FOUND;\n                    // 如果EQ找到并且value已找到\n                } else if (state == State.EQ_FOUND\n                    && (matcher = PARAM_PAIRS_RE.matcher(segment.getSqlSegment())).matches()) {\n                    this.valueKey = matcher.group(1);\n                    this.state = State.VERSION_VALUE_PRESENT;\n                    return true;\n                    // 处理嵌套\n                } else if (segment instanceof Wrapper) {\n                    if (find((Wrapper<?>) segment)) {\n                        return true;\n                    }\n                    // 判断字段是否是要查找字段\n                } else if (segment.getSqlSegment().equals(this.fieldName)) {\n                    this.state = State.FIELD_FOUND;\n                }\n            }\n            return false;\n        }\n    }\n\n    private static class VersionFactory {\n\n        /**\n         * 存放版本号类型与获取更新后版本号的map\n         */\n        private static final Map<Class<?>, Function<Object, Object>> VERSION_FUNCTION_MAP = new HashMap<>();\n\n        static {\n            VERSION_FUNCTION_MAP.put(long.class, version -> (long) version + 1);\n            VERSION_FUNCTION_MAP.put(Long.class, version -> (long) version + 1);\n            VERSION_FUNCTION_MAP.put(int.class, version -> (int) version + 1);\n            VERSION_FUNCTION_MAP.put(Integer.class, version -> (int) version + 1);\n            VERSION_FUNCTION_MAP.put(Date.class, version -> new Date());\n            VERSION_FUNCTION_MAP.put(Timestamp.class, version -> new Timestamp(System.currentTimeMillis()));\n            VERSION_FUNCTION_MAP.put(LocalDateTime.class, version -> LocalDateTime.now());\n            VERSION_FUNCTION_MAP.put(Instant.class, version -> Instant.now());\n        }\n\n        public static Object getUpdatedVersionVal(Class<?> clazz, Object originalVersionVal) {\n            Function<Object, Object> versionFunction = VERSION_FUNCTION_MAP.get(clazz);\n            if (versionFunction == null) {\n                // not supported type, return original val.\n                return originalVersionVal;\n            }\n            return versionFunction.apply(originalVersionVal);\n        }\n    }\n\n    /**\n     * This method provides the control for version value.<BR>\n     * Returned value type must be the same as original one.\n     *\n     * @param originalVersionVal ignore\n     * @return updated version val\n     */\n    protected Object getUpdatedVersionVal(Class<?> clazz, Object originalVersionVal) {\n        return VersionFactory.getUpdatedVersionVal(clazz, originalVersionVal);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/ReplacePlaceholderInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n * 功能类似于 {@link GlobalConfig.DbConfig#isReplacePlaceholder()},\n * 只是这个是在运行时实时替换,适用范围更广\n *\n * @author miemie\n * @since 2020-11-19\n */\npublic class ReplacePlaceholderInnerInterceptor implements InnerInterceptor {\n\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n    private String escapeSymbol;\n\n    public ReplacePlaceholderInnerInterceptor() {\n    }\n\n    public ReplacePlaceholderInnerInterceptor(String escapeSymbol) {\n        this.escapeSymbol = escapeSymbol;\n    }\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        String sql = boundSql.getSql();\n        List<String> find = SqlUtils.findPlaceholder(sql);\n        if (CollectionUtils.isNotEmpty(find)) {\n            sql = SqlUtils.replaceSqlPlaceholder(sql, find, escapeSymbol);\n            PluginUtils.mpBoundSql(boundSql).sql(sql);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 扩展的各种插件\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.plugins;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/DialectFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.*;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * 分页方言工厂类\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class DialectFactory {\n\n    private static final Map<DbType, IDialect> DIALECT_ENUM_MAP = new EnumMap<>(DbType.class);\n\n    public static IDialect getDialect(DbType dbType) {\n        IDialect dialect = DIALECT_ENUM_MAP.get(dbType);\n        if (null == dialect) {\n            if (dbType == DbType.OTHER) {\n                throw ExceptionUtils.mpe(\"%s database not supported.\", dbType.getDb());\n            }\n            // mysql same type\n            else if (dbType.mysqlSameType()) {\n                dialect = new MySqlDialect();\n            }\n            // oracle same type\n            else if (dbType.oracleSameType()) {\n                dialect = new OracleDialect();\n            }\n            // postgresql same type\n            else if (dbType.postgresqlSameType()) {\n                dialect = new PostgreDialect();\n            }\n            // other types\n            else if (dbType == DbType.ORACLE_12C\n                || dbType == DbType.FIREBIRD\n                || dbType == DbType.SQL_SERVER\n                || dbType == DbType.DERBY) {\n                dialect = new Oracle12cDialect();\n            } else if (dbType == DbType.DB2) {\n                dialect = new DB2Dialect();\n            } else if (dbType == DbType.SQL_SERVER2005) {\n                dialect = new SQLServer2005Dialect();\n            } else if (dbType == DbType.SYBASE) {\n                dialect = new SybaseDialect();\n            } else if (dbType == DbType.XCloud) {\n                dialect = new XCloudDialect();\n            } else if (dbType == DbType.GBASE_8S\n                || dbType == DbType.GBASEDBT\n                || dbType == DbType.GBASE_INFORMIX\n                || dbType == DbType.SINODB) {\n                dialect = new GBase8sDialect();\n            } else if (dbType == DbType.INFORMIX) {\n                dialect = new InformixDialect();\n            } else if (dbType == DbType.TRINO\n                || dbType == DbType.PRESTO) {\n                dialect = new TrinoDialect();\n            } else if (dbType == DbType.HIVE2) {\n                dialect = new Hive2Dialect();\n            } else if (dbType == DbType.GAUSS_DB) {\n                dialect = new GaussDBDialect();\n            }\n            DIALECT_ENUM_MAP.put(dbType, dialect);\n        }\n        return dialect;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/DialectModel.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport lombok.Getter;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.session.Configuration;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\n/**\n * 分页参数动态化所需 model\n * <p>\n * 动态入参的模式暂只支持在sql末端如: select * from x limit ?,?\n *\n * @author miemie\n * @since 2018-10-31\n */\npublic class DialectModel {\n    private static final String FIRST_PARAM_NAME = \"mybatis_plus_first\";\n    private static final String SECOND_PARAM_NAME = \"mybatis_plus_second\";\n\n    /**\n     * 分页方言 sql\n     */\n    @Getter\n    private final String dialectSql;\n    /**\n     * 提供 Configuration\n     */\n    private Configuration configuration;\n    /**\n     * 用 List<ParameterMapping> 消费第一个值\n     */\n    private Consumer<List<ParameterMapping>> firstParamConsumer = i -> {\n    };\n    /**\n     * 用 Map<String, Object> 消费第一个值\n     */\n    private Consumer<Map<String, Object>> firstParamMapConsumer = i -> {\n    };\n    /**\n     * 用 List<ParameterMapping> 消费第二个值\n     */\n    private Consumer<List<ParameterMapping>> secondParamConsumer = i -> {\n    };\n    /**\n     * 用 Map<String, Object> 消费第二个值\n     */\n    private Consumer<Map<String, Object>> secondParamMapConsumer = i -> {\n    };\n    /**\n     * 提供 第一个值\n     */\n    private final long firstParam;\n    /**\n     * 提供 第二个值\n     */\n    private final long secondParam;\n\n    public DialectModel(String dialectSql) {\n        this(dialectSql, 0, 0);\n    }\n\n    public DialectModel(String dialectSql, long firstParam) {\n        this(dialectSql, firstParam, 0);\n    }\n\n    public DialectModel(String dialectSql, long firstParam, long secondParam) {\n        this.dialectSql = dialectSql;\n        this.firstParam = firstParam;\n        this.secondParam = secondParam;\n    }\n\n    /**\n     * 设置消费 List<ParameterMapping> 的方式\n     * <p>带下标的</p>\n     * <p>mark: 标记一下,暂时没看到哪个数据库的分页方言会存在使用该方法</p>\n     *\n     * @return this\n     */\n    @SuppressWarnings(\"unused\")\n    public DialectModel setConsumer(boolean isFirstParam, Function<List<ParameterMapping>, Integer> function) {\n        if (isFirstParam) {\n            this.firstParamConsumer = i -> i.add(function.apply(i), new ParameterMapping\n                .Builder(configuration, FIRST_PARAM_NAME, long.class).build());\n        } else {\n            this.secondParamConsumer = i -> i.add(function.apply(i), new ParameterMapping\n                .Builder(configuration, SECOND_PARAM_NAME, long.class).build());\n        }\n        this.setParamMapConsumer(isFirstParam);\n        return this;\n    }\n\n    /**\n     * 设置消费 List<ParameterMapping> 的方式\n     * <p>不带下标的</p>\n     *\n     * @return this\n     */\n    public DialectModel setConsumer(boolean isFirstParam) {\n        if (isFirstParam) {\n            this.firstParamConsumer = i -> i.add(new ParameterMapping.Builder(configuration, FIRST_PARAM_NAME, long.class).build());\n        } else {\n            this.secondParamConsumer = i -> i.add(new ParameterMapping.Builder(configuration, SECOND_PARAM_NAME, long.class).build());\n        }\n        this.setParamMapConsumer(isFirstParam);\n        return this;\n    }\n\n    /**\n     * 设置消费 List<ParameterMapping> 的方式\n     * <p>不带下标的,两个值都有</p>\n     *\n     * @return this\n     */\n    public DialectModel setConsumerChain() {\n        return setConsumer(true).setConsumer(false);\n    }\n\n    /**\n     * 把内部所有需要消费的都消费掉\n     *\n     * @param parameterMappings    ParameterMapping 集合\n     * @param configuration        Configuration\n     * @param additionalParameters additionalParameters map\n     */\n    public void consumers(List<ParameterMapping> parameterMappings, Configuration configuration,\n                          Map<String, Object> additionalParameters) {\n        Assert.notNull(configuration, \"configuration must notNull !\");\n        Assert.notNull(parameterMappings, \"parameterMappings must notNull !\");\n        Assert.notNull(additionalParameters, \"additionalParameters must notNull !\");\n        this.configuration = configuration;\n        this.firstParamConsumer.accept(parameterMappings);\n        this.secondParamConsumer.accept(parameterMappings);\n        this.firstParamMapConsumer.accept(additionalParameters);\n        this.secondParamMapConsumer.accept(additionalParameters);\n    }\n\n    /**\n     * 设置消费 Map<String, Object> 的方式\n     */\n    private void setParamMapConsumer(boolean isFirstParam) {\n        if (isFirstParam) {\n            this.firstParamMapConsumer = i -> i.put(FIRST_PARAM_NAME, firstParam);\n        } else {\n            this.secondParamMapConsumer = i -> i.put(SECOND_PARAM_NAME, secondParam);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/Page.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination;\n\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport lombok.Setter;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Predicate;\n\n/**\n * 简单分页模型\n *\n * @author hubin\n * @since 2018-06-09\n */\npublic class Page<T> implements IPage<T> {\n\n    private static final long serialVersionUID = 8545996863226528798L;\n\n    /**\n     * 查询数据列表\n     */\n    private List<T> records = Collections.emptyList();\n\n    /**\n     * 总数\n     */\n    private long total = 0;\n    /**\n     * 每页显示条数，默认 10\n     */\n    private long size = 10;\n\n    /**\n     * 当前页\n     */\n    private long current = 1;\n\n    /**\n     * 排序字段信息\n     */\n    @Setter\n    private List<OrderItem> orders = new ArrayList<>();\n\n    /**\n     * 自动优化 COUNT SQL\n     */\n    private boolean optimizeCountSql = true;\n    /**\n     * 是否进行 count 查询\n     */\n    private boolean searchCount = true;\n    /**\n     * {@link #optimizeJoinOfCountSql()}\n     */\n    @Setter\n    private boolean optimizeJoinOfCountSql = true;\n    /**\n     * 单页分页条数限制\n     */\n    @Setter\n    private Long maxLimit;\n    /**\n     * countId\n     */\n    @Setter\n    private String countId;\n\n    public Page() {\n    }\n\n    /**\n     * 分页构造函数\n     *\n     * @param current 当前页\n     * @param size    每页显示条数\n     */\n    public Page(long current, long size) {\n        this(current, size, 0);\n    }\n\n    public Page(long current, long size, long total) {\n        this(current, size, total, true);\n    }\n\n    public Page(long current, long size, boolean searchCount) {\n        this(current, size, 0, searchCount);\n    }\n\n    public Page(long current, long size, long total, boolean searchCount) {\n        if (current > 1) {\n            this.current = current;\n        }\n        this.size = size;\n        this.total = total;\n        this.searchCount = searchCount;\n    }\n\n    /**\n     * 是否存在上一页\n     *\n     * @return true / false\n     */\n    public boolean hasPrevious() {\n        return this.current > 1;\n    }\n\n    /**\n     * 是否存在下一页\n     *\n     * @return true / false\n     */\n    public boolean hasNext() {\n        return this.current < this.getPages();\n    }\n\n    @Override\n    public List<T> getRecords() {\n        return this.records;\n    }\n\n    @Override\n    public Page<T> setRecords(List<T> records) {\n        this.records = records;\n        return this;\n    }\n\n    @Override\n    public long getTotal() {\n        return this.total;\n    }\n\n    @Override\n    public Page<T> setTotal(long total) {\n        this.total = total;\n        return this;\n    }\n\n    @Override\n    public long getSize() {\n        return this.size;\n    }\n\n    @Override\n    public Page<T> setSize(long size) {\n        this.size = size;\n        return this;\n    }\n\n    @Override\n    public long getCurrent() {\n        return this.current;\n    }\n\n    @Override\n    public Page<T> setCurrent(long current) {\n        this.current = current;\n        return this;\n    }\n\n    @Override\n    public String countId() {\n        return this.countId;\n    }\n\n    @Override\n    public Long maxLimit() {\n        return this.maxLimit;\n    }\n\n    /**\n     * 查找 order 中正序排序的字段数组\n     *\n     * @param filter 过滤器\n     * @return 返回正序排列的字段数组\n     */\n    private String[] mapOrderToArray(Predicate<OrderItem> filter) {\n        List<String> columns = new ArrayList<>(orders.size());\n        orders.forEach(i -> {\n            if (filter.test(i)) {\n                columns.add(i.getColumn());\n            }\n        });\n        return columns.toArray(new String[0]);\n    }\n\n    /**\n     * 移除符合条件的条件\n     *\n     * @param filter 条件判断\n     */\n    private void removeOrder(Predicate<OrderItem> filter) {\n        for (int i = orders.size() - 1; i >= 0; i--) {\n            if (filter.test(orders.get(i))) {\n                orders.remove(i);\n            }\n        }\n    }\n\n    /**\n     * 添加新的排序条件，构造条件可以使用工厂：{@link OrderItem#build(String, boolean)}\n     *\n     * @param items 条件\n     * @return 返回分页参数本身\n     */\n    public Page<T> addOrder(OrderItem... items) {\n        orders.addAll(Arrays.asList(items));\n        return this;\n    }\n\n    /**\n     * 添加新的排序条件，构造条件可以使用工厂：{@link OrderItem#build(String, boolean)}\n     *\n     * @param items 条件\n     * @return 返回分页参数本身\n     */\n    public Page<T> addOrder(List<OrderItem> items) {\n        orders.addAll(items);\n        return this;\n    }\n\n    @Override\n    public List<OrderItem> orders() {\n        return this.orders;\n    }\n\n    @Override\n    public boolean optimizeCountSql() {\n        return optimizeCountSql;\n    }\n\n    public static <T> Page<T> of(long current, long size, long total, boolean searchCount) {\n        return new Page<>(current, size, total, searchCount);\n    }\n\n    @Override\n    public boolean optimizeJoinOfCountSql() {\n        return optimizeJoinOfCountSql;\n    }\n\n    public Page<T> setSearchCount(boolean searchCount) {\n        this.searchCount = searchCount;\n        return this;\n    }\n\n    public Page<T> setOptimizeCountSql(boolean optimizeCountSql) {\n        this.optimizeCountSql = optimizeCountSql;\n        return this;\n    }\n\n    @Override\n    public long getPages() {\n        // 解决 github issues/3208\n        return IPage.super.getPages();\n    }\n\n    /* --------------- 以下为静态构造方式 --------------- */\n    public static <T> Page<T> of(long current, long size) {\n        return of(current, size, 0);\n    }\n\n    public static <T> Page<T> of(long current, long size, long total) {\n        return of(current, size, total, true);\n    }\n\n    public static <T> Page<T> of(long current, long size, boolean searchCount) {\n        return of(current, size, 0, searchCount);\n    }\n\n    @Override\n    public boolean searchCount() {\n        if (total < 0) {\n            return false;\n        }\n        return searchCount;\n    }\n\n    @Override\n    public String toString() {\n        return \"Page{\" +\n            \"records=\" + records +\n            \", total=\" + total +\n            \", size=\" + size +\n            \", current=\" + current +\n            \", orders=\" + orders +\n            \", optimizeCountSql=\" + optimizeCountSql +\n            \", searchCount=\" + searchCount +\n            \", optimizeJoinOfCountSql=\" + optimizeJoinOfCountSql +\n            \", maxLimit=\" + maxLimit +\n            \", countId='\" + countId + '\\'' +\n            '}';\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/PageDTO.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\n\nimport java.util.List;\n\n/**\n * 简单分页模型 DTO 用于解决跨服务数据传输问题，不影响 Page 作为返回对象序列化 JSON 产生不必要的数据\n *\n * @author hubin\n * @since 2021-05-20\n */\npublic class PageDTO<T> extends Page<T> {\n\n    private static final long serialVersionUID = 1L;\n\n    public PageDTO() {\n    }\n\n    public PageDTO(long current, long size) {\n        this(current, size, 0);\n    }\n\n    public PageDTO(long current, long size, long total) {\n        this(current, size, total, true);\n    }\n\n    public PageDTO(long current, long size, boolean searchCount) {\n        this(current, size, 0, searchCount);\n    }\n\n    public PageDTO(long current, long size, long total, boolean searchCount) {\n        super(current, size, total, searchCount);\n    }\n\n    /* --------------- 以下为静态构造方式 --------------- */\n    public static <T> Page<T> of(long current, long size) {\n        return of(current, size, 0);\n    }\n\n    public static <T> Page<T> of(long current, long size, long total) {\n        return of(current, size, total, true);\n    }\n\n    public static <T> Page<T> of(long current, long size, boolean searchCount) {\n        return of(current, size, 0, searchCount);\n    }\n\n    public static <T> Page<T> of(long current, long size, long total, boolean searchCount) {\n        return new PageDTO<>(current, size, total, searchCount);\n    }\n\n    public String getCountId() {\n        return super.countId();\n    }\n\n    public Long getMaxLimit() {\n        return super.maxLimit();\n    }\n\n    public List<OrderItem> getOrders() {\n        return super.orders();\n    }\n\n    public boolean isOptimizeCountSql() {\n        return super.optimizeCountSql();\n    }\n\n    public boolean isSearchCount() {\n        return super.searchCount();\n    }\n\n    public boolean isOptimizeJoinOfCountSql() {\n        return super.optimizeJoinOfCountSql();\n    }\n\n    @Override\n    public String toString() {\n        return \"PageDTO{} \" + super.toString();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/DB2Dialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * DB2 数据库分页方言\n *\n * @author hubin\n * @since 2016-11-10\n */\npublic class DB2Dialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        long firstParam = offset + 1;\n        long secondParam = offset + limit;\n        String sql = \"SELECT * FROM (SELECT TMP_PAGE.*,ROWNUMBER() OVER() AS ROW_ID FROM ( \" + originalSql +\n            \" ) AS TMP_PAGE) TMP_PAGE WHERE ROW_ID BETWEEN \" + FIRST_MARK + \" AND \" + SECOND_MARK;\n        return new DialectModel(sql, firstParam, secondParam).setConsumerChain();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/GBase8sDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * GBase 8s V8.8 数据库分页语句组装实现\n * 通用分页版本\n *\n * @author liaojinqing\n * @since 2021-07-20\n */\npublic class GBase8sDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql)\n            .insert(6, \" SKIP \" + offset + \" FIRST \" + limit);\n        return new DialectModel(sql.toString());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/GaussDBDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * GaussDB 数据库分页实现\n * <p>分页语法格式: SELECT query_list FROM table_name [ LIMIT { [offset,] count | ALL } ]\n *\n * @author nieqiurong\n * @since 3.5.13\n */\npublic class GaussDBDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql).append(\" LIMIT \").append(FIRST_MARK);\n        if (offset != 0L) {\n            sql.append(StringPool.COMMA).append(SECOND_MARK);\n            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();\n        } else {\n            return new DialectModel(sql.toString(), limit).setConsumer(true);\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/Hive2Dialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * Hive2分页方言\n *\n * @author dongwei\n * @since 3.5.11\n */\npublic class Hive2Dialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        long firstParam = offset + 1;\n        String sql = \"SELECT a.* FROM (SELECT TMP_PAGE.*,ROW_NUMBER() OVER() AS ROW_ID FROM ( \"\n            + originalSql +\n            \" ) TMP_PAGE) a OFFSET \"\n            + firstParam\n            + \" ROWS FETCH NEXT \"\n            + limit + \" ROWS ONLY\";\n        return new DialectModel(sql);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/IDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * 数据库 分页语句组装接口\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic interface IDialect {\n    /**\n     * 这俩没什么特殊意义\n     * 只是为了实现类方便使用,以及区分分页 sql 的参数\n     */\n    String FIRST_MARK = StringPool.QUESTION_MARK;\n    String SECOND_MARK = StringPool.QUESTION_MARK;\n\n    /**\n     * 组装分页语句\n     *\n     * @param originalSql 原始语句\n     * @param offset      偏移量\n     * @param limit       界限\n     * @return 分页模型\n     */\n    DialectModel buildPaginationSql(String originalSql, long offset, long limit);\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/InformixDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\npublic class InformixDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder ret = new StringBuilder();\n        ret.append(String.format(\"select skip %s first %s \", offset + \"\", limit + \"\"));\n        ret.append(originalSql.replaceFirst(\"(?i)select\", \"\"));\n        return new DialectModel(ret.toString());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/MySqlDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * MYSQL 数据库分页语句组装实现\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class MySqlDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql).append(\" LIMIT \").append(FIRST_MARK);\n        if (offset != 0L) {\n            sql.append(StringPool.COMMA).append(SECOND_MARK);\n            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();\n        } else {\n            return new DialectModel(sql.toString(), limit).setConsumer(true);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/Oracle12cDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * ORACLE 新版数据库分页语句组装实现\n *\n * @author 廖仑辉\n * @since 2019-11-29\n */\npublic class Oracle12cDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        String sql = originalSql + \" OFFSET \" + FIRST_MARK + \" ROWS FETCH NEXT \" + SECOND_MARK + \" ROWS ONLY\";\n        return new DialectModel(sql, offset, limit).setConsumerChain();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/OracleDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * ORACLE 数据库分页语句组装实现\n * 通用分页版本\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class OracleDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        limit = (offset >= 1) ? (offset + limit) : limit;\n        String sql = \"SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( \" +\n            originalSql + \" ) TMP WHERE ROWNUM <=\" + FIRST_MARK + \") WHERE ROW_ID > \" + SECOND_MARK;\n        return new DialectModel(sql, limit, offset).setConsumerChain();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/PostgreDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * Postgre 数据库分页语句组装实现\n *\n * @author hubin\n * @since 2016-01-23\n */\npublic class PostgreDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql).append(\" LIMIT \").append(FIRST_MARK);\n        if (offset != 0L) {\n            sql.append(\" OFFSET \").append(SECOND_MARK);\n            return new DialectModel(sql.toString(), limit, offset).setConsumerChain();\n        } else {\n            return new DialectModel(sql.toString(), limit).setConsumer(true);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SQLServer2005Dialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * SQLServer 2005 数据库分页方言\n *\n * @author hubin\n * @since 2016-11-10\n */\npublic class SQLServer2005Dialect implements IDialect {\n\n    private static final Pattern ORDER_BY_PATTERN = Pattern.compile(\"\\\\((.)*order by(.)*\\\\)\");\n\n    private static final Pattern SELECT_PATTERN = Pattern.compile(\"(?i)select\\\\s+(distinct\\\\s+)?\");\n\n    public String getOrderByPart(String sql) {\n        String order_by = \"order by\";\n        int lastIndex = sql.toLowerCase().lastIndexOf(order_by);\n        if (lastIndex == -1) {\n            return StringPool.EMPTY;\n        }\n        Matcher matcher = ORDER_BY_PATTERN.matcher(sql);\n        if (!matcher.find()) {\n            return sql.substring(lastIndex);\n        }\n        int end = matcher.end();\n        return lastIndex < end ? StringPool.EMPTY : sql.substring(lastIndex);\n    }\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder pagingBuilder = new StringBuilder();\n        String orderby = getOrderByPart(originalSql);\n        String distinctStr = StringPool.EMPTY;\n        String sqlPartString = originalSql;\n        Matcher matcher = SELECT_PATTERN.matcher(originalSql);\n        if (matcher.find()) {\n            int index = matcher.end() - 1;\n            if (matcher.group().toLowerCase().contains(\"distinct\")) {\n                distinctStr = \"DISTINCT \";\n            }\n            sqlPartString = sqlPartString.substring(index);\n        }\n        pagingBuilder.append(sqlPartString);\n        // if no ORDER BY is specified use fake ORDER BY field to avoid errors\n        if (StringUtils.isBlank(orderby)) {\n            orderby = \"ORDER BY CURRENT_TIMESTAMP\";\n        }\n        long firstParam = offset + 1;\n        long secondParam = offset + limit;\n        String sql = \"WITH selectTemp AS (SELECT \" + distinctStr + \"TOP 100 PERCENT \" +\n            \" ROW_NUMBER() OVER (\" + orderby + \") as __row_number__, \" + pagingBuilder +\n            \") SELECT * FROM selectTemp WHERE __row_number__ BETWEEN \" +\n            //FIX#299：原因：mysql中limit 10(offset,size) 是从第10开始（不包含10）,；而这里用的BETWEEN是两边都包含，所以改为offset+1\n            firstParam + \" AND \" + secondParam + \" ORDER BY __row_number__\";\n        return new DialectModel(sql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SQLServerDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\n/**\n * SQLServer 数据库分页语句组装实现\n *\n * @author hubin\n * @since 2016-03-23\n */\n@Deprecated\npublic class SQLServerDialect extends Oracle12cDialect {\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/SybaseDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * sybase 数据库分页方言\n *\n * @author sdsyjh\n * @since 2020-07-30\n */\npublic class SybaseDialect implements IDialect {\n\n    private final boolean hasTop; // sybase12.5.4以前，不支持select top\n\n    public SybaseDialect() {\n        this(false);\n    }\n\n    public SybaseDialect(boolean hasTop) {\n        this.hasTop = hasTop;\n    }\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        int index = findMainFROM(originalSql);\n        if(index == -1){\n            index = originalSql.toUpperCase().indexOf(\" FROM \");\n        }\n        String sql = \"select\";\n        if (hasTop) {\n            sql += \" top \" + (offset + limit);\n        }\n        sql += \" rownum=identity(12),\" + originalSql.substring(6, index) + \" into #t \" + originalSql.substring(index);\n        sql += \" select * from #t where rownum > ? and rownum <= ? \";\n        sql += \"drop table #t \";\n        return new DialectModel(sql, offset, offset + limit).setConsumerChain();\n    }\n\n    /**\n     * 查找主查询的FROM位置\n     * @param sql   需要查找的SQL\n     * @return  FROM位置的起始下标\n     * @author lroyia\n     * @since  2022年6月15日 17:57:28\n     */\n    private int findMainFROM(String sql){\n        String tempSql = sql.toUpperCase();\n        tempSql = tempSql.replace(\"\\n\", \" \").replace(\"\\t\", \" \").replace(\"\\r\", \" \");\n        Matcher select_ = Pattern.compile(\"SELECT \").matcher(tempSql);\n        Matcher from_ = Pattern.compile(\" FROM \").matcher(tempSql);\n        List<Integer> selectIndex = new ArrayList<>(10);\n        List<Integer> fromIndex = new ArrayList<>(10);\n        while (select_.find()){\n            int start = select_.start();\n            if(start == 0 || tempSql.charAt(start - 1) == ' '|| tempSql.charAt(start - 1) == '('){\n                selectIndex.add(start);\n            }\n        }\n        while (from_.find()){\n            fromIndex.add(from_.start());\n        }\n\n        // 形成select与from的混合顺序下标列表\n        List<Integer> indexList = new ArrayList<>(20);\n        indexList.addAll(selectIndex);\n        indexList.addAll(fromIndex);\n        indexList.sort(Comparator.naturalOrder());\n        // 无法匹配有效下标\n        if(indexList.size() < 2){\n            return -1;\n        }\n        // 利用栈逻辑匹配select与from\n        int selectCount = 1;\n        for(int i = 1; i < indexList.size(); i++){\n            int each = indexList.get(i);\n            if(fromIndex.contains(each)){\n                // pointer弹栈\n                selectCount--;\n            }else{\n                // pointer压栈\n                selectCount++;\n            }\n            // from将全部select弹出，代表当前这个from为主要from\n            if(selectCount == 0){\n                return each;\n            }\n        }\n        return -1;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/TrinoDialect.java",
    "content": "package com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * Trino 数据库分页语句组装实现\n *\n * @author hushunbo\n * @since 2023-10-06\n */\npublic class TrinoDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql);\n        if (offset != 0L) {\n            sql.append(\" OFFSET \").append(FIRST_MARK);\n            sql.append(\" LIMIT \").append(SECOND_MARK);\n            return new DialectModel(sql.toString(), offset, limit).setConsumerChain();\n        } else {\n            sql.append(\" LIMIT \").append(FIRST_MARK);\n            return new DialectModel(sql.toString(), limit).setConsumer(true);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/XCloudDialect.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\n\n/**\n * XCloud 数据库分页语句组装实现\n *\n * @author huyang\n * @since 2022-04-13\n */\npublic class XCloudDialect implements IDialect {\n\n    @Override\n    public DialectModel buildPaginationSql(String originalSql, long offset, long limit) {\n        StringBuilder sql = new StringBuilder(originalSql).append(\" LIMIT \");\n        if (offset != 0L) {\n            sql.append(\" (\" + FIRST_MARK + \",\" + SECOND_MARK + \") \");\n            return new DialectModel(sql.toString(), offset + 1, offset + limit).setConsumerChain();\n        } else {\n            sql.append(FIRST_MARK);\n            return new DialectModel(sql.toString(), limit).setConsumer(true);\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/dialects/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * mybatis 分页插件，支持不同数据库方言实现类\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination.dialects;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/pagination/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 分页相关类\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.plugins.pagination;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/repository/AbstractRepository.java",
    "content": "package com.baomidou.mybatisplus.extension.repository;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata;\nimport com.baomidou.mybatisplus.core.toolkit.MybatisUtils;\nimport com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\npublic abstract class AbstractRepository<M extends BaseMapper<T>, T>  implements IRepository<T> {\n\n    protected final Log log = LogFactory.getLog(getClass());\n\n    /**\n     * @see #getEntityClass()\n     */\n    private Class<T> entityClass;\n\n    @Override\n    public Class<T> getEntityClass() {\n        if(this.entityClass == null) {\n            this.entityClass = (Class<T>) GenericTypeUtils.resolveTypeArguments(this.getMapperClass(), BaseMapper.class)[0];\n        }\n        return this.entityClass;\n    }\n\n    /**\n     *  @see #getMapperClass()\n     */\n    private Class<M> mapperClass;\n\n    private volatile SqlSessionFactory sqlSessionFactory;\n\n    protected SqlSessionFactory getSqlSessionFactory() {\n        if (this.sqlSessionFactory == null) {\n            MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this.getBaseMapper());\n            this.sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mapperProxyMetadata.getSqlSession());\n        }\n        return this.sqlSessionFactory;\n    }\n\n    /**\n     * @return baseMapper 真实类型\n     * @since 3.5.7\n     */\n    public Class<M> getMapperClass() {\n        if (this.mapperClass == null) {\n            MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this.getBaseMapper());\n            this.mapperClass = (Class<M>) mapperProxyMetadata.getMapperInterface();\n        }\n        return this.mapperClass;\n    }\n\n    /**\n     * TableId 注解存在更新记录，否插入一条记录\n     *\n     * @param entity 实体对象\n     * @return boolean\n     */\n    @Override\n    public boolean saveOrUpdate(T entity) {\n        return getBaseMapper().insertOrUpdate(entity);\n    }\n\n    @Override\n    public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {\n        return getBaseMapper().selectOne(queryWrapper, throwEx);\n    }\n\n    @Override\n    public Optional<T> getOneOpt(Wrapper<T> queryWrapper, boolean throwEx) {\n        return Optional.ofNullable(getBaseMapper().selectOne(queryWrapper, throwEx));\n    }\n\n    @Override\n    public Map<String, Object> getMap(Wrapper<T> queryWrapper) {\n        return SqlHelper.getObject(log, getBaseMapper().selectMaps(queryWrapper));\n    }\n\n    @Override\n    public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {\n        return SqlHelper.getObject(log, listObjs(queryWrapper, mapper));\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param list      数据集合\n     * @param batchSize 批量大小\n     * @param consumer  执行方法\n     * @param <E>       泛型\n     * @return 操作结果\n     * @since 3.3.1\n     */\n    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {\n        return SqlHelper.executeBatch(getSqlSessionFactory(), this.log, list, batchSize, consumer);\n    }\n\n    /**\n     * 执行批量操作（默认批次提交数量{@link IRepository#DEFAULT_BATCH_SIZE}）\n     *\n     * @param list     数据集合\n     * @param consumer 执行方法\n     * @param <E>      泛型\n     * @return 操作结果\n     * @since 3.3.1\n     */\n    protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {\n        return executeBatch(list, DEFAULT_BATCH_SIZE, consumer);\n    }\n\n    @Override\n    public boolean removeById(Serializable id, boolean useFill) {\n        return SqlHelper.retBool(getBaseMapper().deleteById(id, useFill));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/repository/IRepository.java",
    "content": "package com.baomidou.mybatisplus.extension.repository;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\npublic interface IRepository<T> {\n\n    /**\n     * 默认批次提交数量\n     */\n    int DEFAULT_BATCH_SIZE = Constants.DEFAULT_BATCH_SIZE;\n\n    /**\n     * 插入一条记录（选择字段，策略插入）\n     *\n     * @param entity 实体对象\n     */\n    default boolean save(T entity) {\n        return SqlHelper.retBool(getBaseMapper().insert(entity));\n    }\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     */\n    boolean saveBatch(Collection<T> entityList, int batchSize);\n\n    /**\n     * 批量修改插入\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  每次的数量\n     */\n    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param id 主键ID\n     */\n    default boolean removeById(Serializable id) {\n        return SqlHelper.retBool(getBaseMapper().deleteById(id));\n    }\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param id      主键(类型必须与实体类型字段保持一致)\n     * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除)\n     * @return 删除结果\n     * @since 3.5.0\n     */\n    default boolean removeById(Serializable id, boolean useFill) {\n        throw new UnsupportedOperationException(\"不支持的方法!\");\n    }\n\n    /**\n     * 根据实体(ID)删除\n     *\n     * @param entity 实体\n     * @since 3.4.4\n     */\n    default boolean removeById(T entity) {\n        return SqlHelper.retBool(getBaseMapper().deleteById(entity));\n    }\n\n    /**\n     * 根据 columnMap 条件，删除记录\n     *\n     * @param columnMap 表字段 map 对象\n     */\n    default boolean removeByMap(Map<String, Object> columnMap) {\n        Assert.notEmpty(columnMap, \"error: columnMap must not be empty\");\n        return SqlHelper.retBool(getBaseMapper().deleteByMap(columnMap));\n    }\n\n    /**\n     * 根据 entity 条件，删除记录\n     *\n     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default boolean remove(Wrapper<T> queryWrapper) {\n        return SqlHelper.retBool(getBaseMapper().delete(queryWrapper));\n    }\n\n    /**\n     * 删除（根据ID 批量删除）\n     *\n     * @param list 主键ID或实体列表\n     */\n    default boolean removeByIds(Collection<?> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return false;\n        }\n        return SqlHelper.retBool(getBaseMapper().deleteByIds(list));\n    }\n\n    /**\n     * 批量删除\n     *\n     * @param list    主键ID或实体列表\n     * @param useFill 是否填充(为true的情况,会将入参转换实体进行delete删除)\n     * @return 删除结果\n     * @since 3.5.0\n     */\n    default boolean removeByIds(Collection<?> list, boolean useFill) {\n        if (CollectionUtils.isEmpty(list)) {\n            return false;\n        }\n        return SqlHelper.retBool(getBaseMapper().deleteByIds(list, useFill));\n    }\n\n    /**\n     * 根据 ID 选择修改\n     *\n     * @param entity 实体对象\n     */\n    default boolean updateById(T entity) {\n        return SqlHelper.retBool(getBaseMapper().updateById(entity));\n    }\n\n    /**\n     * 根据 UpdateWrapper 条件，更新记录 需要设置sqlset\n     * <p>此方法无法进行自动填充,如需自动填充请使用{@link #update(Object, Wrapper)}</p>\n     *\n     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}\n     */\n    default boolean update(Wrapper<T> updateWrapper) {\n        return update(null, updateWrapper);\n    }\n\n    /**\n     * 根据 whereEntity 条件，更新记录\n     *\n     * @param entity        实体对象(当entity为空时无法进行自动填充)\n     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}\n     */\n    default boolean update(T entity, Wrapper<T> updateWrapper) {\n        return SqlHelper.retBool(getBaseMapper().update(entity, updateWrapper));\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  更新批次数量\n     */\n    boolean updateBatchById(Collection<T> entityList, int batchSize);\n\n    /**\n     * TableId 注解存在更新记录，否插入一条记录\n     *\n     * @param entity 实体对象\n     */\n    boolean saveOrUpdate(T entity);\n\n    /**\n     * 根据 ID 查询\n     *\n     * @param id 主键ID\n     */\n    default T getById(Serializable id) {\n        return getBaseMapper().selectById(id);\n    }\n\n    /**\n     * 根据 ID 查询，返回一个Option对象\n     *\n     * @param id 主键ID\n     * @return {@link Optional}\n     */\n    default Optional<T> getOptById(Serializable id) {\n        return Optional.ofNullable(getBaseMapper().selectById(id));\n    }\n\n    /**\n     * 查询（根据ID 批量查询）\n     *\n     * @param idList 主键ID列表\n     */\n    default List<T> listByIds(Collection<? extends Serializable> idList) {\n        return getBaseMapper().selectByIds(idList);\n    }\n\n    /**\n     * 查询（根据 columnMap 条件）\n     *\n     * @param columnMap 表字段 map 对象\n     */\n    default List<T> listByMap(Map<String, Object> columnMap) {\n        return getBaseMapper().selectByMap(columnMap);\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录 <br/>\n     * <p>结果集，如果是多个会抛出异常，随机取一条加上限制条件 wrapper.last(\"LIMIT 1\")</p>\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default T getOne(Wrapper<T> queryWrapper) {\n        return getOne(queryWrapper, true);\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录 <br/>\n     * <p>结果集，如果是多个会抛出异常，随机取一条加上限制条件 wrapper.last(\"LIMIT 1\")</p>\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @return {@link Optional} 返回一个Optional对象\n     */\n    default Optional<T> getOneOpt(Wrapper<T> queryWrapper) {\n        return getOneOpt(queryWrapper, true);\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param throwEx      有多个 result 是否抛出异常\n     */\n    T getOne(Wrapper<T> queryWrapper, boolean throwEx);\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param throwEx      有多个 result 是否抛出异常\n     * @return {@link Optional} 返回一个Optional对象\n     */\n    Optional<T> getOneOpt(Wrapper<T> queryWrapper, boolean throwEx);\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    Map<String, Object> getMap(Wrapper<T> queryWrapper);\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param mapper       转换函数\n     */\n    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);\n\n    /**\n     * 查询指定条件是否存在数据\n     *\n     * @see Wrappers#emptyWrapper()\n     */\n    default boolean exists(Wrapper<T> queryWrapper) {\n        return getBaseMapper().exists(queryWrapper);\n    }\n\n    /**\n     * 查询总记录数\n     *\n     * @see Wrappers#emptyWrapper()\n     */\n    default long count() {\n        return count(Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询总记录数\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default long count(Wrapper<T> queryWrapper) {\n        return SqlHelper.retCount(getBaseMapper().selectCount(queryWrapper));\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default List<T> list(Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectList(queryWrapper);\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param page         分页条件\n     * @param queryWrapper queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    default List<T> list(IPage<T> page, Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectList(page, queryWrapper);\n    }\n\n    /**\n     * 查询所有\n     *\n     * @see Wrappers#emptyWrapper()\n     */\n    default List<T> list() {\n        return list(Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 分页查询单表数据\n     *\n     * @param page 分页条件\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    default List<T> list(IPage<T> page) {\n        return list(page, Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 翻页查询\n     *\n     * @param page         翻页对象\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectPage(page, queryWrapper);\n    }\n\n    /**\n     * 无条件翻页查询\n     *\n     * @param page 翻页对象\n     * @see Wrappers#emptyWrapper()\n     */\n    default <E extends IPage<T>> E page(E page) {\n        return page(page, Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectMaps(queryWrapper);\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param page         分页条件\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    default List<Map<String, Object>> listMaps(IPage<? extends Map<String, Object>> page, Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectMaps(page, queryWrapper);\n    }\n\n\n    /**\n     * 查询所有列表\n     *\n     * @see Wrappers#emptyWrapper()\n     */\n    default List<Map<String, Object>> listMaps() {\n        return listMaps(Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param page 分页条件\n     * @see Wrappers#emptyWrapper()\n     */\n    default List<Map<String, Object>> listMaps(IPage<? extends Map<String, Object>> page) {\n        return listMaps(page, Wrappers.emptyWrapper());\n    }\n\n\n    /**\n     * 查询全部记录\n     */\n    default <E> List<E> listObjs() {\n        return getBaseMapper().selectObjs(null);\n    }\n\n    /**\n     * 查询全部记录\n     *\n     * @param mapper 转换函数\n     */\n    default <V> List<V> listObjs(Function<? super Object, V> mapper) {\n        return listObjs(Wrappers.emptyWrapper(), mapper);\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default <E> List<E> listObjs(Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectObjs(queryWrapper);\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param mapper       转换函数\n     */\n    default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {\n        return getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());\n    }\n\n    /**\n     * 翻页查询\n     *\n     * @param page         翻页对象\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {\n        return getBaseMapper().selectMapsPage(page, queryWrapper);\n    }\n\n    /**\n     * 无条件翻页查询\n     *\n     * @param page 翻页对象\n     * @see Wrappers#emptyWrapper()\n     */\n    default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {\n        return pageMaps(page, Wrappers.emptyWrapper());\n    }\n\n    /**\n     * 获取对应 entity 的 BaseMapper\n     *\n     * @return BaseMapper\n     */\n    BaseMapper<T> getBaseMapper();\n\n    /**\n     * 获取 entity 的 class\n     *\n     * @return {@link Class<T>}\n     */\n    Class<T> getEntityClass();\n\n    /*\n     * 以下的方法使用介绍:\n     * <p>\n     * 一. 名称介绍\n     * 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作\n     * 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的\n     * </p>\n     * <p>\n     * 二. 支持介绍\n     * 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作\n     * 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作\n     * </p>\n     *\n     * <p>\n     * 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推\n     * 1. 根据条件获取一条数据: `query().eq(\"column\", value).one()`\n     * 2. 根据条件删除一条数据: `update().eq(\"column\", value).remove()`\n     * </p>\n     *\n     */\n\n    /**\n     * 链式查询 普通\n     *\n     * @return QueryWrapper 的包装类\n     */\n    default QueryChainWrapper<T> query() {\n        return ChainWrappers.queryChain(getBaseMapper());\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaQueryWrapper 的包装类\n     */\n    default LambdaQueryChainWrapper<T> lambdaQuery() {\n        return ChainWrappers.lambdaQueryChain(getBaseMapper(), getEntityClass());\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @param entity 实体对象\n     * @return LambdaQueryWrapper 的包装类\n     */\n    default LambdaQueryChainWrapper<T> lambdaQuery(T entity) {\n        return ChainWrappers.lambdaQueryChain(getBaseMapper(), entity);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * kotlin 使用\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    default KtQueryChainWrapper<T> ktQuery() {\n        return ChainWrappers.ktQueryChain(getBaseMapper(), getEntityClass());\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * kotlin 使用\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    default KtUpdateChainWrapper<T> ktUpdate() {\n        return ChainWrappers.ktUpdateChain(getBaseMapper(), getEntityClass());\n    }\n\n    /**\n     * 链式更改 普通\n     *\n     * @return UpdateWrapper 的包装类\n     */\n    default UpdateChainWrapper<T> update() {\n        return ChainWrappers.updateChain(getBaseMapper());\n    }\n\n    /**\n     * 链式更改 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaUpdateWrapper 的包装类\n     */\n    default LambdaUpdateChainWrapper<T> lambdaUpdate() {\n        return ChainWrappers.lambdaUpdateChain(getBaseMapper());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/scripting/MybatisFreeMarkerLanguageDriver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.scripting;\n\nimport com.baomidou.mybatisplus.core.MybatisParameterHandler;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver;\nimport org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig;\n\n/**\n * @author miemie\n * @since 2020-06-18\n */\n@NoArgsConstructor\npublic class MybatisFreeMarkerLanguageDriver extends FreeMarkerLanguageDriver {\n\n    public MybatisFreeMarkerLanguageDriver(FreeMarkerLanguageDriverConfig driverConfig) {\n        super(driverConfig);\n    }\n\n    @Override\n    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {\n        return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/scripting/MybatisThymeleafLanguageDriver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.scripting;\n\nimport com.baomidou.mybatisplus.core.MybatisParameterHandler;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver;\nimport org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig;\nimport org.thymeleaf.ITemplateEngine;\n\n/**\n * @author miemie\n * @since 2020-06-18\n */\n@NoArgsConstructor\npublic class MybatisThymeleafLanguageDriver extends ThymeleafLanguageDriver {\n\n    public MybatisThymeleafLanguageDriver(ThymeleafLanguageDriverConfig config) {\n        super(config);\n    }\n\n    public MybatisThymeleafLanguageDriver(ITemplateEngine templateEngine) {\n        super(templateEngine);\n    }\n\n    @Override\n    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {\n        return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/scripting/MybatisVelocityLanguageDriver.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.scripting;\n\nimport com.baomidou.mybatisplus.core.MybatisParameterHandler;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.mybatis.scripting.velocity.VelocityLanguageDriver;\nimport org.mybatis.scripting.velocity.VelocityLanguageDriverConfig;\n\n/**\n * @author miemie\n * @since 2020-06-18\n */\n@NoArgsConstructor\npublic class MybatisVelocityLanguageDriver extends VelocityLanguageDriver {\n\n    public MybatisVelocityLanguageDriver(VelocityLanguageDriverConfig driverConfig) {\n        super(driverConfig);\n    }\n\n    @Override\n    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {\n        return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/spi/CompatibleHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.spi;\n\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.util.ServiceLoader;\n\n/**\n * 兼容处理辅助类\n * <p>默认加载使用SPI实现,需要手动指定请使用{@link #setCompatibleSet(CompatibleSet)}</p>\n *\n * @see com.baomidou.mybatisplus.core.spi.CompatibleHelper\n * @deprecated 3.5.12\n */\n@Deprecated\npublic class CompatibleHelper {\n\n    private static final Log LOG = LogFactory.getLog(CompatibleHelper.class);\n\n    private static CompatibleSet COMPATIBLE_SET = null;\n\n    static {\n        ServiceLoader<CompatibleSet> loader = ServiceLoader.load(CompatibleSet.class, CompatibleSet.class.getClassLoader());\n        int size = 0;\n        for (CompatibleSet compatibleSet : loader) {\n            size++;\n            LOG.debug(\"Load compatibleSet: \" + compatibleSet);\n            COMPATIBLE_SET = compatibleSet;\n        }\n        if (size > 1) {\n            LOG.warn(\"There are currently multiple implementations, and the last one is used \" + COMPATIBLE_SET);\n        }\n    }\n\n    /**\n     * 判断是否存在 {@link com.baomidou.mybatisplus.extension.spi.CompatibleSet} 实例\n     *\n     * @return 是否存在 (存在返回true,为空返回false)\n     * @since 3.5.12\n     */\n    public static boolean hasCompatibleSet() {\n        return COMPATIBLE_SET != null;\n    }\n\n    /**\n     * 手动指定 {@link com.baomidou.mybatisplus.extension.spi.CompatibleSet} 实例\n     *\n     * @param compatibleSet {@link com.baomidou.mybatisplus.extension.spi.CompatibleSet} 实例\n     * @since 3.5.12\n     */\n    public static void setCompatibleSet(CompatibleSet compatibleSet) {\n        COMPATIBLE_SET = compatibleSet;\n    }\n\n    /**\n     * 获取{@link com.baomidou.mybatisplus.extension.spi.CompatibleSet}实例\n     * <p>当为空时会抛出异常,需要检查是否为空请使用{@link #hasCompatibleSet()}</p>\n     *\n     * @return {@link com.baomidou.mybatisplus.extension.spi.CompatibleSet}\n     * @see #setCompatibleSet(CompatibleSet)\n     */\n    public static CompatibleSet getCompatibleSet() {\n        Assert.isTrue(hasCompatibleSet(), \"Please add specific implementation dependencies or use the setCompatibleSet method to specify\");\n        return COMPATIBLE_SET;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/spi/CompatibleSet.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.spi;\n\n/**\n * Web 开发平台待兼容方法集接口类\n *\n * @see com.baomidou.mybatisplus.core.spi.CompatibleSet\n * @deprecated 3.5.12\n */\n@Deprecated\npublic interface CompatibleSet extends com.baomidou.mybatisplus.core.spi.CompatibleSet {\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/ChainWrappers.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;\n\n/**\n * 快捷构造 chain 式调用的工具类\n *\n * @author miemie\n * @since 2019-11-28\n * @since 3.3.0\n */\npublic final class ChainWrappers {\n\n    private ChainWrappers() {\n        // ignore\n    }\n\n    /**\n     * 链式查询 普通\n     *\n     * @return QueryWrapper 的包装类\n     */\n    public static <T> QueryChainWrapper<T> queryChain(BaseMapper<T> mapper) {\n        return new QueryChainWrapper<>(mapper);\n    }\n\n    public static <T> QueryChainWrapper<T> queryChain(Class<T> entityClass) {\n        return new QueryChainWrapper<>(entityClass);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaQueryWrapper 的包装类\n     */\n    public static <T> LambdaQueryChainWrapper<T> lambdaQueryChain(BaseMapper<T> mapper) {\n        return new LambdaQueryChainWrapper<>(mapper);\n    }\n\n    public static <T> LambdaQueryChainWrapper<T> lambdaQueryChain(Class<T> entityClass) {\n        return new LambdaQueryChainWrapper<>(entityClass);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaQueryWrapper 的包装类\n     */\n    public static <T> LambdaQueryChainWrapper<T> lambdaQueryChain(BaseMapper<T> mapper, T entity) {\n        return new LambdaQueryChainWrapper<>(mapper, entity);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaQueryWrapper 的包装类\n     */\n    public static <T> LambdaQueryChainWrapper<T> lambdaQueryChain(BaseMapper<T> mapper, Class<T> entityClass) {\n        return new LambdaQueryChainWrapper<>(mapper, entityClass);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * 仅支持 Kotlin\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtQueryChainWrapper<T> ktQueryChain(BaseMapper<T> mapper, Class<T> entityClass) {\n        return new KtQueryChainWrapper<>(mapper, entityClass);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * 仅支持 Kotlin\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtQueryChainWrapper<T> ktQueryChain(BaseMapper<T> mapper, T entity) {\n        return new KtQueryChainWrapper<>(mapper, entity);\n    }\n\n    /**\n     * 链式查询 lambda 式\n     * 仅支持 Kotlin\n     * 仅传 entityClass 实体类\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtQueryChainWrapper<T> ktQueryChain(Class<T> entityClass) {\n        return new KtQueryChainWrapper<>(entityClass);\n    }\n\n\n    /**\n     * 链式更改 普通\n     *\n     * @return UpdateWrapper 的包装类\n     */\n    public static <T> UpdateChainWrapper<T> updateChain(BaseMapper<T> mapper) {\n        return new UpdateChainWrapper<>(mapper);\n    }\n\n    public static <T> UpdateChainWrapper<T> updateChain(Class<T> entityClass) {\n        return new UpdateChainWrapper<>(entityClass);\n    }\n\n    /**\n     * 链式更改 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaUpdateWrapper 的包装类\n     */\n    public static <T> LambdaUpdateChainWrapper<T> lambdaUpdateChain(BaseMapper<T> mapper) {\n        return new LambdaUpdateChainWrapper<>(mapper);\n    }\n\n    public static <T> LambdaUpdateChainWrapper<T> lambdaUpdateChain(Class<T> entityClass) {\n        return new LambdaUpdateChainWrapper<>(entityClass);\n    }\n\n    /**\n     * 链式更改 lambda 式\n     * 仅支持 Kotlin\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtUpdateChainWrapper<T> ktUpdateChain(BaseMapper<T> mapper, Class<T> entityClass) {\n        return new KtUpdateChainWrapper<>(mapper, entityClass);\n    }\n\n    /**\n     * 链式更改 lambda 式\n     * 仅支持 Kotlin\n     * 仅传 entityClass 实体类\n     *\n     * @return KtUpdateWrapper 的包装类\n     */\n    public static <T> KtUpdateChainWrapper<T> ktUpdateChain(Class<T> entityClass) {\n        return new KtUpdateChainWrapper<>(entityClass);\n    }\n\n\n    /**\n     * 链式更改 lambda 式\n     * 仅支持 Kotlin\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtUpdateChainWrapper<T> ktUpdateChain(BaseMapper<T> mapper, T entity) {\n        return new KtUpdateChainWrapper<>(mapper, entity);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/Db.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 以静态方式调用Service中的函数\n *\n * @author VampireAchao\n * @since 2022-05-03\n */\npublic class Db {\n\n    private static final Log log = LogFactory.getLog(Db.class);\n\n    private Db() {\n        /* Do not new me! */\n    }\n\n    /**\n     * 插入一条记录（选择字段，策略插入）\n     *\n     * @param entity 实体对象\n     */\n    public static <T> boolean save(T entity) {\n        if (Objects.isNull(entity)) {\n            return false;\n        }\n        Integer result = SqlHelper.execute(getEntityClass(entity), baseMapper -> baseMapper.insert(entity));\n        return SqlHelper.retBool(result);\n    }\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     */\n    public static <T> boolean saveBatch(Collection<T> entityList) {\n        return saveBatch(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  插入批次数量\n     */\n    public static <T> boolean saveBatch(Collection<T> entityList, int batchSize) {\n        if (CollectionUtils.isEmpty(entityList)) {\n            return false;\n        }\n        Class<T> entityClass = getEntityClass(entityList);\n        List<BatchResult> batchResults = SqlHelper.execute(entityClass, baseMapper -> baseMapper.insert(entityList, batchSize));\n        return SqlHelper.retBool(batchResults);\n    }\n\n    /**\n     * 批量修改插入\n     *\n     * @param entityList 实体对象集合\n     */\n    public static <T> boolean saveOrUpdateBatch(Collection<T> entityList) {\n        return saveOrUpdateBatch(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 批量修改插入\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  每次的数量\n     */\n    public static <T> boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {\n        if (CollectionUtils.isEmpty(entityList)) {\n            return false;\n        }\n        Class<T> entityClass = getEntityClass(entityList);\n        List<BatchResult> batchResults = SqlHelper.execute(entityClass, baseMapper -> baseMapper.insertOrUpdate(entityList, batchSize));\n        return SqlHelper.retBool(batchResults);\n    }\n\n    /**\n     * 根据 ID 删除\n     *\n     * @param id          主键ID\n     * @param entityClass 实体类\n     */\n    public static <T> boolean removeById(Serializable id, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> SqlHelper.retBool(baseMapper.deleteById(id)));\n    }\n\n    /**\n     * 根据实体(ID)删除\n     *\n     * @param entity 实体\n     */\n    public static <T> boolean removeById(T entity) {\n        if (Objects.isNull(entity)) {\n            return false;\n        }\n        return SqlHelper.execute(getEntityClass(entity), baseMapper -> SqlHelper.retBool(baseMapper.deleteById(entity)));\n    }\n\n    /**\n     * 根据 entity 条件，删除记录\n     *\n     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> boolean remove(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> SqlHelper.retBool(baseMapper.delete(queryWrapper)));\n    }\n\n    /**\n     * 根据 ID 选择修改\n     *\n     * @param entity 实体对象\n     */\n    public static <T> boolean updateById(T entity) {\n        if (Objects.isNull(entity)) {\n            return false;\n        }\n        return SqlHelper.execute(getEntityClass(entity), baseMapper -> SqlHelper.retBool(baseMapper.updateById(entity)));\n    }\n\n    /**\n     * 根据 UpdateWrapper 条件，更新记录 需要设置sqlset\n     *\n     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}\n     */\n    public static <T> boolean update(AbstractWrapper<T, ?, ?> updateWrapper) {\n        return update(null, updateWrapper);\n    }\n\n    /**\n     * 根据 whereEntity 条件，更新记录\n     *\n     * @param entity        实体对象\n     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}\n     */\n    public static <T> boolean update(T entity, AbstractWrapper<T, ?, ?> updateWrapper) {\n        return SqlHelper.execute(getEntityClass(updateWrapper), baseMapper -> SqlHelper.retBool(baseMapper.update(entity, updateWrapper)));\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     */\n    public static <T> boolean updateBatchById(Collection<T> entityList) {\n        return updateBatchById(entityList, Constants.DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     * @param batchSize  更新批次数量\n     */\n    public static <T> boolean updateBatchById(Collection<T> entityList, int batchSize) {\n        Class<T> entityClass = getEntityClass(entityList);\n        List<BatchResult> batchResults = SqlHelper.execute(entityClass, baseMapper -> baseMapper.updateById(entityList, batchSize));\n        return SqlHelper.retBool(batchResults);\n    }\n\n    /**\n     * 删除（根据ID 批量删除）\n     *\n     * @param list        主键ID或实体列表\n     * @param entityClass 实体类\n     */\n    public static <T> boolean removeByIds(Collection<? extends Serializable> list, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> SqlHelper.retBool(baseMapper.deleteByIds(list)));\n    }\n\n    /**\n     * 根据 columnMap 条件，删除记录\n     *\n     * @param columnMap   表字段 map 对象\n     * @param entityClass 实体类\n     */\n    public static <T> boolean removeByMap(Map<String, Object> columnMap, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> SqlHelper.retBool(baseMapper.deleteByMap(columnMap)));\n    }\n\n    /**\n     * TableId 注解存在更新记录，否插入一条记录\n     *\n     * @param entity 实体对象\n     */\n    public static <T> boolean saveOrUpdate(T entity) {\n        if (Objects.isNull(entity)) {\n            return false;\n        }\n        return SqlHelper.execute(getEntityClass(entity), baseMapper -> baseMapper.insertOrUpdate(entity));\n    }\n\n    /**\n     * 根据 ID 查询\n     *\n     * @param id          主键ID\n     * @param entityClass 实体类\n     */\n    public static <T> T getById(Serializable id, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectById(id));\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录 <br/>\n     * <p>结果集，如果是多个会抛出异常，随机取一条加上限制条件 wrapper.last(\"LIMIT 1\")</p>\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> T getOne(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return getOne(queryWrapper, true);\n    }\n\n    /**\n     * 根据 entity里不为空的字段，查询一条记录 <br/>\n     * <p>结果集，如果是多个会抛出异常，随机取一条加上限制条件 wrapper.last(\"LIMIT 1\")</p>\n     *\n     * @param entity 实体对象\n     */\n    public static <T> T getOne(T entity) {\n        return getOne(Wrappers.lambdaQuery(entity), true);\n    }\n\n    /**\n     * 根据 entity里不为空的字段，查询一条记录\n     *\n     * @param entity  实体对象\n     * @param throwEx 有多个 result 是否抛出异常\n     */\n    public static <T> T getOne(T entity, boolean throwEx) {\n        return getOne(Wrappers.lambdaQuery(entity), throwEx);\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param throwEx      有多个 result 是否抛出异常\n     */\n    public static <T> T getOne(AbstractWrapper<T, ?, ?> queryWrapper, boolean throwEx) {\n        Class<T> entityClass = getEntityClass(queryWrapper);\n        if (throwEx) {\n            return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectOne(queryWrapper));\n        }\n        return SqlHelper.execute(entityClass, baseMapper -> SqlHelper.getObject(log, baseMapper.selectList(queryWrapper)));\n    }\n\n    /**\n     * 查询（根据 columnMap 条件）\n     *\n     * @param columnMap   表字段 map 对象\n     * @param entityClass 实体类\n     */\n    public static <T> List<T> listByMap(Map<String, Object> columnMap, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectByMap(columnMap));\n    }\n\n    /**\n     * 查询（根据ID 批量查询）\n     *\n     * @param idList      主键ID列表\n     * @param entityClass 实体类\n     */\n    public static <T> List<T> listByIds(Collection<? extends Serializable> idList, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectByIds(idList));\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> Map<String, Object> getMap(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> SqlHelper.getObject(log, baseMapper.selectMaps(queryWrapper)));\n    }\n\n    /**\n     * 根据 entity不为空条件，查询一条记录\n     *\n     * @param entity 实体对象\n     */\n    public static <T> Map<String, Object> getMap(T entity) {\n        return getMap(Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 查询总记录数\n     *\n     * @param entityClass 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T> long count(Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> SqlHelper.retCount(baseMapper.selectCount(null)));\n    }\n\n    /**\n     * 根据entity中不为空的数据查询记录数\n     *\n     * @param entity 实体类\n     */\n    public static <T> long count(T entity) {\n        return count(Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询总记录数\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> long count(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> SqlHelper.retCount(baseMapper.selectCount(queryWrapper)));\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> List<T> list(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectList(queryWrapper));\n    }\n\n    /**\n     * @param page         分页条件\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param <T>          entity\n     * @return 列表数据\n     */\n    public static <T> List<T> list(IPage<T> page, AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectList(page, queryWrapper));\n    }\n\n\n    /**\n     * 查询所有\n     *\n     * @param entityClass 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T> List<T> list(Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectList(null));\n    }\n\n    /**\n     * @param page        分页条件\n     * @param entityClass 实体类\n     * @param <T>         entity\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    public static <T> List<T> list(IPage<T> page, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectList(page, null));\n    }\n\n\n    /**\n     * 根据entity中不为空的字段进行查询\n     *\n     * @param entity 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T> List<T> list(T entity) {\n        return list(Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 根据entity中不为空的字段进行查询\n     *\n     * @param page   分页条件\n     * @param entity 实体类\n     * @param <T>    entity\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    public static <T> List<T> list(IPage<T> page, T entity) {\n        return list(page, Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> List<Map<String, Object>> listMaps(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectMaps(queryWrapper));\n    }\n\n    /**\n     * @since 3.5.3.2\n     * @param page 分页参数\n     * @param queryWrapper queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @return 列表数据\n     * @param <T> entity\n     */\n    public static <T> List<Map<String, Object>> listMaps(IPage<? extends Map<String, Object>> page, AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectMaps(page, queryWrapper));\n    }\n\n    /**\n     * 查询所有列表\n     *\n     * @param entityClass 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T> List<Map<String, Object>> listMaps(Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectMaps(null));\n    }\n\n    /**\n     * 分页查询列表\n     *\n     * @param page        分页条件\n     * @param entityClass 实体类\n     * @param <T>         entity\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    public static <T> List<Map<String, Object>> listMaps(IPage<? extends Map<String, Object>> page, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectMaps(page, null));\n    }\n\n\n    /**\n     * 根据entity不为空的条件查询列表\n     *\n     * @param entity 实体类\n     */\n    public static <T> List<Map<String, Object>> listMaps(T entity) {\n        return listMaps(Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 根据entity不为空的条件查询列表\n     *\n     * @param page   分页条件\n     * @param entity entity\n     * @param <T>    entity\n     * @return 列表数据\n     * @since 3.5.3.2\n     */\n    public static <T> List<Map<String, Object>> listMaps(IPage<? extends Map<String, Object>> page, T entity) {\n        return listMaps(page, Wrappers.lambdaQuery(entity));\n    }\n\n    /**\n     * 查询全部记录\n     *\n     * @param entityClass 实体类\n     */\n    public static <T> List<T> listObjs(Class<T> entityClass) {\n        return listObjs(entityClass, i -> i);\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <E, T> List<E> listObjs(AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectObjs(queryWrapper));\n    }\n\n    /**\n     * 根据 Wrapper 条件，查询全部记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param mapper       转换函数\n     */\n    public static <T, V> List<V> listObjs(AbstractWrapper<T, ?, ?> queryWrapper, SFunction<? super T, V> mapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectList(queryWrapper).stream().map(mapper).collect(Collectors.toList()));\n    }\n\n    /**\n     * 查询全部记录\n     *\n     * @param entityClass 实体类\n     * @param mapper      转换函数\n     */\n    public static <T, V> List<V> listObjs(Class<T> entityClass, SFunction<? super T, V> mapper) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectList(null).stream().map(mapper).collect(Collectors.toList()));\n    }\n\n    /**\n     * 无条件翻页查询\n     *\n     * @param page        翻页对象\n     * @param entityClass 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T, E extends IPage<Map<String, Object>>> E pageMaps(E page, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectMapsPage(page, null));\n    }\n\n    /**\n     * 翻页查询\n     *\n     * @param page         翻页对象\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T, E extends IPage<Map<String, Object>>> E pageMaps(E page, AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectMapsPage(page, queryWrapper));\n    }\n\n    /**\n     * 无条件翻页查询\n     *\n     * @param page        翻页对象\n     * @param entityClass 实体类\n     * @see Wrappers#emptyWrapper()\n     */\n    public static <T> IPage<T> page(IPage<T> page, Class<T> entityClass) {\n        return SqlHelper.execute(entityClass, baseMapper -> baseMapper.selectPage(page, null));\n    }\n\n    /**\n     * 翻页查询\n     *\n     * @param page         翻页对象\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     */\n    public static <T> IPage<T> page(IPage<T> page, AbstractWrapper<T, ?, ?> queryWrapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> baseMapper.selectPage(page, queryWrapper));\n    }\n\n    /**\n     * 链式查询 普通\n     *\n     * @return QueryWrapper 的包装类\n     */\n    public static <T> QueryChainWrapper<T> query(Class<T> entityClass) {\n        return ChainWrappers.queryChain(entityClass);\n    }\n\n    /**\n     * kt链式查询\n     *\n     * @return KtQueryWrapper 的包装类\n     */\n    public static <T> KtQueryChainWrapper<T> ktQuery(Class<T> entityClass) {\n        return ChainWrappers.ktQueryChain(entityClass);\n    }\n\n\n    /**\n     * 链式查询 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaQueryWrapper 的包装类\n     */\n    public static <T> LambdaQueryChainWrapper<T> lambdaQuery(Class<T> entityClass) {\n        return ChainWrappers.lambdaQueryChain(entityClass);\n    }\n\n    /**\n     * 链式更改 普通\n     *\n     * @return UpdateWrapper 的包装类\n     */\n    public static <T> UpdateChainWrapper<T> update(Class<T> entityClass) {\n        return ChainWrappers.updateChain(entityClass);\n    }\n\n    /**\n     * kt链式更改\n     *\n     * @return KtUpdateWrapper 的包装类\n     */\n    public static <T> KtUpdateChainWrapper<T> ktUpdate(Class<T> entityClass) {\n        return ChainWrappers.ktUpdateChain(entityClass);\n    }\n\n    /**\n     * 链式更改 lambda 式\n     * <p>注意：不支持 Kotlin </p>\n     *\n     * @return LambdaUpdateWrapper 的包装类\n     */\n    public static <T> LambdaUpdateChainWrapper<T> lambdaUpdate(Class<T> entityClass) {\n        return ChainWrappers.lambdaUpdateChain(entityClass);\n    }\n\n    /**\n     * <p>\n     * 根据updateWrapper尝试更新，否继续执行saveOrUpdate(T)方法\n     * 此次修改主要是减少了此项业务代码的代码量（存在性验证之后的saveOrUpdate操作）\n     * </p>\n     *\n     * @param entity 实体对象\n     */\n    public static <T> boolean saveOrUpdate(T entity, AbstractWrapper<T, ?, ?> updateWrapper) {\n        return update(entity, updateWrapper) || saveOrUpdate(entity);\n    }\n\n    /**\n     * 根据 Wrapper，查询一条记录\n     *\n     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}\n     * @param mapper       转换函数\n     */\n    public static <T, V> V getObj(AbstractWrapper<T, ?, ?> queryWrapper, SFunction<? super T, V> mapper) {\n        return SqlHelper.execute(getEntityClass(queryWrapper), baseMapper -> mapper.apply(baseMapper.selectOne(queryWrapper)));\n    }\n\n    /**\n     * 从集合中获取实体类型\n     *\n     * @param entityList 实体集合\n     * @param <T>        实体类型\n     * @return 实体类型\n     */\n    protected static <T> Class<T> getEntityClass(Collection<T> entityList) {\n        Class<T> entityClass = null;\n        for (T entity : entityList) {\n            if (entity != null) {\n                entityClass = getEntityClass(entity);\n                break;\n            }\n        }\n        Assert.notNull(entityClass, \"error: can not get entityClass from entityList\");\n        return entityClass;\n    }\n\n    /**\n     * 从wrapper中尝试获取实体类型\n     *\n     * @param queryWrapper 条件构造器\n     * @param <T>          实体类型\n     * @return 实体类型\n     */\n    protected static <T> Class<T> getEntityClass(AbstractWrapper<T, ?, ?> queryWrapper) {\n        Class<T> entityClass = queryWrapper.getEntityClass();\n        if (entityClass == null) {\n            T entity = queryWrapper.getEntity();\n            if (entity != null) {\n                entityClass = getEntityClass(entity);\n            }\n        }\n        Assert.notNull(entityClass, \"error: can not get entityClass from wrapper\");\n        return entityClass;\n    }\n\n    /**\n     * 从entity中尝试获取实体类型\n     *\n     * @param entity 实体\n     * @param <T>    实体类型\n     * @return 实体类型\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected static <T> Class<T> getEntityClass(T entity) {\n        return (Class<T>) entity.getClass();\n    }\n\n\n    /**\n     * 获取表信息，获取不到报错提示\n     *\n     * @param entityClass 实体类\n     * @param <T>         实体类型\n     * @return 对应表信息\n     */\n    protected static <T> TableInfo getTableInfo(Class<T> entityClass) {\n        return Optional.ofNullable(TableInfoHelper.getTableInfo(entityClass)).orElseThrow(() -> ExceptionUtils.mpe(\"error: can not find TableInfo from Class: \\\"%s\\\".\", entityClass.getName()));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/JdbcUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.regex.Pattern;\n\n/**\n * JDBC 工具类\n *\n * @author nieqiurong\n * @since 2016-12-05\n */\npublic class JdbcUtils {\n\n    private static final Log logger = LogFactory.getLog(JdbcUtils.class);\n    private static final Map<String, DbType> JDBC_DB_TYPE_CACHE = new ConcurrentHashMap<>();\n\n    /**\n     * 不关闭 Connection,因为是从事务里获取的,sqlSession会负责关闭\n     *\n     * @param executor Executor\n     * @return DbType\n     */\n    public static DbType getDbType(Executor executor) {\n        try {\n            Connection conn = executor.getTransaction().getConnection();\n            return CollectionUtils.computeIfAbsent(JDBC_DB_TYPE_CACHE, conn.getMetaData().getURL(), JdbcUtils::getDbType);\n        } catch (SQLException e) {\n            throw ExceptionUtils.mpe(e);\n        }\n    }\n\n    /**\n     * 根据连接地址判断数据库类型\n     *\n     * @param jdbcUrl 连接地址\n     * @return ignore\n     */\n    public static DbType getDbType(String jdbcUrl) {\n        Assert.isFalse(StringUtils.isBlank(jdbcUrl), \"Error: The jdbcUrl is Null, Cannot read database type\");\n        String url = jdbcUrl.toLowerCase();\n        if (url.contains(\":mysql:\") || url.contains(\":cobar:\")) {\n            return DbType.MYSQL;\n        } else if (url.contains(\":mariadb:\")) {\n            return DbType.MARIADB;\n        } else if (url.contains(\":oracle:\")) {\n            return DbType.ORACLE;\n        } else if (url.contains(\":sqlserver:\") || url.contains(\":microsoft:\")) {\n            return DbType.SQL_SERVER;\n        } else if (url.contains(\":postgresql:\")) {\n            return DbType.POSTGRE_SQL;\n        } else if (url.contains(\":hsqldb:\")) {\n            return DbType.HSQL;\n        } else if (url.contains(\":db2:\")) {\n            return DbType.DB2;\n        } else if (url.contains(\":sqlite:\")) {\n            return DbType.SQLITE;\n        } else if (url.contains(\":h2:\")) {\n            return DbType.H2;\n        } else if (url.contains(\":lealone:\")) {\n            return DbType.LEALONE;\n        } else if (regexFind(\":dm\\\\d*:\", url)) {\n            return DbType.DM;\n        } else if (url.contains(\":xugu:\")) {\n            return DbType.XU_GU;\n        } else if (regexFind(\":kingbase\\\\d*:\", url)) {\n            return DbType.KINGBASE_ES;\n        } else if (url.contains(\":phoenix:\")) {\n            return DbType.PHOENIX;\n        } else if (url.contains(\":zenith:\")) {\n            return DbType.GAUSS;\n        } else if (url.contains(\":gaussdb:\")) {\n            return DbType.GAUSS_DB;\n        } else if (url.contains(\":gbase:\")) {\n            return DbType.GBASE;\n        } else if (url.contains(\":gbasedbt-sqli:\") || url.contains(\":informix-sqli:\")) {\n            return DbType.GBASE_8S;\n        } else if (url.contains(\":gbase8s-pg:\")) {\n            return DbType.GBASE8S_PG;\n        } else if (url.contains(\":gbase8c:\")) {\n            return DbType.GBASE_8C;\n        } else if (url.contains(\":ch:\") || url.contains(\":clickhouse:\")) {\n            return DbType.CLICK_HOUSE;\n        } else if (url.contains(\":oscar:\")) {\n            return DbType.OSCAR;\n        } else if (url.contains(\":sybase:\")) {\n            return DbType.SYBASE;\n        } else if (url.contains(\":oceanbase:\")) {\n            return DbType.OCEAN_BASE;\n        } else if (url.contains(\":highgo:\")) {\n            return DbType.HIGH_GO;\n        } else if (url.contains(\":cubrid:\")) {\n            return DbType.CUBRID;\n        } else if (url.contains(\":sundb:\")) {\n            return DbType.SUNDB;\n        } else if (url.contains(\":sap:\")) {\n            return DbType.SAP_HANA;\n        } else if (url.contains(\":impala:\")) {\n            return DbType.IMPALA;\n        } else if (url.contains(\":vertica:\")) {\n            return DbType.VERTICA;\n        } else if (url.contains(\":xcloud:\")) {\n            return DbType.XCloud;\n        } else if (url.contains(\":firebirdsql:\")) {\n            return DbType.FIREBIRD;\n        } else if (url.contains(\":redshift:\")) {\n            return DbType.REDSHIFT;\n        } else if (url.contains(\":opengauss:\")) {\n            return DbType.OPENGAUSS;\n        } else if (url.contains(\":taos:\") || url.contains(\":taos-rs:\") || url.contains(\":taos-ws:\")) {\n            return DbType.TDENGINE;\n        } else if (url.contains(\":informix\")) {\n            return DbType.INFORMIX;\n        } else if (url.contains(\":sinodb\")) {\n            return DbType.SINODB;\n        } else if (url.contains(\":uxdb:\")) {\n            return DbType.UXDB;\n        } else if (url.contains(\":trino:\")) {\n            return DbType.TRINO;\n        } else if (url.contains(\":presto:\")) {\n            return DbType.PRESTO;\n        } else if (url.contains(\":derby:\")) {\n            return DbType.DERBY;\n        } else if (url.contains(\":vastbase:\")) {\n            return DbType.VASTBASE;\n        } else if (url.contains(\":goldendb:\")) {\n            return DbType.GOLDENDB;\n        } else if (url.contains(\":duckdb:\")) {\n            return DbType.DUCKDB;\n        } else if (url.contains(\":yasdb:\")) {\n            return DbType.YASDB;\n        } else if (url.contains(\":hive2:\") || url.contains(\":inceptor2:\")) {\n            return DbType.HIVE2;\n        } else {\n            logger.warn(\"The jdbcUrl is \" + jdbcUrl + \", Mybatis Plus Cannot Read Database type or The Database's Not Supported!\");\n            return DbType.OTHER;\n        }\n    }\n\n    /**\n     * 正则匹配\n     *\n     * @param regex 正则\n     * @param input 字符串\n     * @return 验证成功返回 true，验证失败返回 false\n     */\n    public static boolean regexFind(String regex, CharSequence input) {\n        if (null == input) {\n            return false;\n        }\n        return Pattern.compile(regex).matcher(input).find();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/PropertyMapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.AccessLevel;\nimport lombok.RequiredArgsConstructor;\n\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * @author miemie\n * @since 2020-06-15\n */\n@RequiredArgsConstructor(access = AccessLevel.PRIVATE)\npublic class PropertyMapper {\n    private final Properties delegate;\n\n    public static PropertyMapper newInstance(Properties properties) {\n        return new PropertyMapper(properties);\n    }\n\n    public Set<String> keys() {\n        return delegate.stringPropertyNames();\n    }\n\n    public PropertyMapper whenNotBlank(String key, Consumer<String> consumer) {\n        String value = delegate.getProperty(key);\n        if (StringUtils.isNotBlank(value)) {\n            consumer.accept(value);\n        }\n        return this;\n    }\n\n    public <T> PropertyMapper whenNotBlank(String key, Function<String, T> function, Consumer<T> consumer) {\n        String value = delegate.getProperty(key);\n        if (StringUtils.isNotBlank(value)) {\n            consumer.accept(function.apply(value));\n        }\n        return this;\n    }\n\n    /**\n     * mp 内部规则分组\n     *\n     * @return 分组\n     */\n    public Map<String, Properties> group(String group) {\n        final Set<String> keys = keys();\n        Set<String> inner = keys.stream().filter(i -> i.startsWith(group)).collect(Collectors.toSet());\n        if (CollectionUtils.isEmpty(inner)) {\n            return Collections.emptyMap();\n        }\n        Map<String, Properties> map = CollectionUtils.newHashMap();\n        inner.forEach(i -> {\n            Properties p = new Properties();\n            String key = i.substring(group.length()) + StringPool.COLON;\n            int keyIndex = key.length();\n            keys.stream().filter(j -> j.startsWith(key)).forEach(j -> p.setProperty(j.substring(keyIndex), delegate.getProperty(j)));\n            map.put(delegate.getProperty(i), p);\n        });\n        return map;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SimpleQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.BinaryOperator;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Collector;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.LambdaUtils;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\n\n/**\n * simple-query 让简单的查询更简单\n *\n * @author VampireAchao\n * @since 2021/11/9 18:27\n */\npublic class SimpleQuery {\n    private SimpleQuery() {\n        /* Do not new me! */\n    }\n\n    /**\n     * 通过lambda获取Class\n     *\n     * @param sFunction 可序列化的lambda\n     * @param <E>       Class类型\n     * @return 对应的Class\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <E> Class<E> getType(SFunction<E, ?> sFunction) {\n        return (Class<E>) LambdaUtils.extract(sFunction).getInstantiatedClass();\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <E, A> Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {\n        return list2Map(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, Function.identity(), peeks);\n    }\n\n    /**\n     * 传入Wrappers和key，从数据库中根据条件查询出对应的列表，封装成Map\n     *\n     * @param wrapper    条件构造器\n     * @param sFunction  key\n     * @param isParallel 是否并行流\n     * @param peeks      封装成map时可能需要的后续操作，不需要可以不传\n     * @param <E>        实体类型\n     * @param <A>        实体中的属性类型\n     * @return Map<实体中的属性, 实体>\n     */\n    @SafeVarargs\n    public static <E, A> Map<A, E> keyMap(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks) {\n        return list2Map(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, Function.identity(), isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <E, A, P> Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, Consumer<E>... peeks) {\n        return list2Map(Db.list(wrapper.setEntityClass(getType(keyFunc))), keyFunc, valueFunc, peeks);\n    }\n\n    /**\n     * 传入Wrappers和key，从数据库中根据条件查询出对应的列表，封装成Map\n     *\n     * @param wrapper    条件构造器\n     * @param keyFunc    key\n     * @param valueFunc  value\n     * @param isParallel 是否并行流\n     * @param peeks      封装成map时可能需要的后续操作，不需要可以不传\n     * @param <E>        实体类型\n     * @param <A>        实体中的属性类型\n     * @param <P>        实体中的属性类型\n     * @return Map<实体中的属性, 实体>\n     */\n    @SafeVarargs\n    public static <E, A, P> Map<A, P> map(LambdaQueryWrapper<E> wrapper, SFunction<E, A> keyFunc, SFunction<E, P> valueFunc, boolean isParallel, Consumer<E>... peeks) {\n        return list2Map(Db.list(wrapper.setEntityClass(getType(keyFunc))), keyFunc, valueFunc, isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <E, A> Map<A, List<E>> group(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {\n        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <T, K> Map<K, List<T>> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks) {\n        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <T, K, D, A> Map<K, D> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<T, A, D> downstream, Consumer<T>... peeks) {\n        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, downstream, false, peeks);\n    }\n\n    /**\n     * 传入Wrappers和key，从数据库中根据条件查询出对应的列表，封装成Map\n     *\n     * @param wrapper    条件构造器\n     * @param sFunction  分组依据\n     * @param downstream 下游操作\n     * @param isParallel 是否并行流\n     * @param peeks      后续操作\n     * @param <T>        实体类型\n     * @param <K>        实体中的分组依据对应类型，也是Map中key的类型\n     * @param <D>        下游操作对应返回类型，也是Map中value的类型\n     * @param <A>        下游操作在进行中间操作时对应类型\n     * @return Map<实体中的属性, List < 实体>>\n     */\n    @SafeVarargs\n    public static <T, K, D, A> Map<K, D> group(LambdaQueryWrapper<T> wrapper, SFunction<T, K> sFunction, Collector<T, A, D> downstream, boolean isParallel, Consumer<T>... peeks) {\n        return listGroupBy(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, downstream, isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <E, A> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {\n        return list2List(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, peeks);\n    }\n\n    /**\n     * 传入wrappers和需要的某一列，从数据中根据条件查询出对应的列，转换成list\n     *\n     * @param wrapper    条件构造器\n     * @param sFunction  需要的列\n     * @param isParallel 是否并行流\n     * @param peeks      后续操作\n     * @return java.util.List<A>\n     * @since 2021/11/9 17:59\n     */\n    @SafeVarargs\n    public static <E, A> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks) {\n        return list2List(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <A, E> List<A> list2List(List<E> list, SFunction<E, A> sFunction, Consumer<E>... peeks) {\n        return list2List(list, sFunction, false, peeks);\n    }\n\n    /**\n     * 对list进行map、peek操作\n     *\n     * @param list       数据\n     * @param sFunction  需要的列\n     * @param isParallel 是否并行流\n     * @param peeks      后续操作\n     * @return java.util.List<A>\n     * @since 2021/11/9 18:01\n     */\n    @SafeVarargs\n    public static <A, E> List<A> list2List(List<E> list, SFunction<E, A> sFunction, boolean isParallel, Consumer<E>... peeks) {\n        return peekStream(list, isParallel, peeks).map(sFunction).collect(Collectors.toList());\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <K, T> Map<K, List<T>> listGroupBy(List<T> list, SFunction<T, K> sFunction, Consumer<T>... peeks) {\n        return listGroupBy(list, sFunction, false, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <K, T> Map<K, List<T>> listGroupBy(List<T> list, SFunction<T, K> sFunction, boolean isParallel, Consumer<T>... peeks) {\n        return listGroupBy(list, sFunction, Collectors.toList(), isParallel, peeks);\n    }\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <T, K, D, A> Map<K, D> listGroupBy(List<T> list, SFunction<T, K> sFunction, Collector<T, A, D> downstream, Consumer<T>... peeks) {\n        return listGroupBy(list, sFunction, downstream, false, peeks);\n    }\n\n    /**\n     * 对list进行groupBy操作\n     *\n     * @param list       数据\n     * @param sFunction  分组的key，依据\n     * @param downstream 下游操作\n     * @param isParallel 是否并行流\n     * @param peeks      封装成map时可能需要的后续操作，不需要可以不传\n     * @param <T>        实体类型\n     * @param <K>        实体中的分组依据对应类型，也是Map中key的类型\n     * @param <D>        下游操作对应返回类型，也是Map中value的类型\n     * @param <A>        下游操作在进行中间操作时对应类型\n     * @return Map<实体中的属性, List < 实体>>\n     */\n    @SafeVarargs\n    @SuppressWarnings(\"unchecked\")\n    public static <T, K, D, A> Map<K, D> listGroupBy(List<T> list, SFunction<T, K> sFunction, Collector<T, A, D> downstream, boolean isParallel, Consumer<T>... peeks) {\n        boolean hasFinished = downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH);\n        return peekStream(list, isParallel, peeks).collect(new Collector<T, HashMap<K, A>, Map<K, D>>() {\n            @Override\n            public Supplier<HashMap<K, A>> supplier() {\n                return HashMap::new;\n            }\n\n            @Override\n            public BiConsumer<HashMap<K, A>, T> accumulator() {\n                return (m, t) -> {\n                    // 只此一处，和原版groupingBy修改只此一处，成功在支持下游操作的情况下支持null值\n                    K key = Optional.ofNullable(t).map(sFunction).orElse(null);\n                    A container = m.computeIfAbsent(key, k -> downstream.supplier().get());\n                    downstream.accumulator().accept(container, t);\n                };\n            }\n\n            @Override\n            public BinaryOperator<HashMap<K, A>> combiner() {\n                return (m1, m2) -> {\n                    for (Map.Entry<K, A> e : m2.entrySet()) {\n                        m1.merge(e.getKey(), e.getValue(), downstream.combiner());\n                    }\n                    return m1;\n                };\n            }\n\n            @Override\n            public Function<HashMap<K, A>, Map<K, D>> finisher() {\n                return hasFinished ? i -> (Map<K, D>) i : intermediate -> {\n                    intermediate.replaceAll((k, v) -> (A) downstream.finisher().apply(v));\n                    @SuppressWarnings(\"unchecked\")\n                    Map<K, D> castResult = (Map<K, D>) intermediate;\n                    return castResult;\n                };\n            }\n\n            @Override\n            public Set<Characteristics> characteristics() {\n                return hasFinished ? Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)) : Collections.emptySet();\n            }\n        });\n    }\n\n\n    /**\n     * ignore\n     */\n    @SafeVarargs\n    public static <E, A, P> Map<A, P> list2Map(List<E> list, SFunction<E, A> keyFunc, Function<E, P> valueFunc, Consumer<E>... peeks) {\n        return list2Map(list, keyFunc, valueFunc, false, peeks);\n    }\n\n    /**\n     * list转换为map\n     *\n     * @param <E>        实体类型\n     * @param <A>        实体中的属性类型\n     * @param <P>        实体中的属性类型\n     * @param list       数据\n     * @param keyFunc    key\n     * @param isParallel 是否并行流\n     * @param peeks      封装成map时可能需要的后续操作，不需要可以不传\n     * @return Map<实体中的属性, 实体>\n     */\n    @SafeVarargs\n    public static <E, A, P> Map<A, P> list2Map(List<E> list, SFunction<E, A> keyFunc, Function<E, P> valueFunc, boolean isParallel, Consumer<E>... peeks) {\n        return peekStream(list, isParallel, peeks).collect(HashMap::new, (m, v) -> m.put(keyFunc.apply(v), valueFunc.apply(v)), HashMap::putAll);\n    }\n\n    /**\n     * 将list转为Stream流，然后再叠加peek操作\n     *\n     * @param list       数据\n     * @param isParallel 是否并行流\n     * @param peeks      叠加的peek操作\n     * @param <E>        数据元素类型\n     * @return 转换后的流\n     */\n    @SafeVarargs\n    public static <E> Stream<E> peekStream(List<E> list, boolean isParallel, Consumer<E>... peeks) {\n        if (CollectionUtils.isEmpty(list)) {\n            return Stream.empty();\n        }\n        return Stream.of(peeks).reduce(StreamSupport.stream(list.spliterator(), isParallel), Stream::peek, Stream::concat);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SqlHelper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport com.baomidou.mybatisplus.core.spi.CompatibleSet;\nimport org.apache.ibatis.executor.BatchResult;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\n\nimport java.sql.Statement;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiPredicate;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.IntStream;\n\n/**\n * SQL 辅助类\n *\n * @author hubin\n * @since 2016-11-06\n */\npublic final class SqlHelper {\n\n    /**\n     * 主要用于 service 和 ar\n     */\n    public static SqlSessionFactory FACTORY;\n\n    /**\n     * 批量操作 SqlSession\n     *\n     * @param clazz 实体类\n     * @return SqlSession\n     * @deprecated 3.5.4\n     */\n    @Deprecated\n    public static SqlSession sqlSessionBatch(Class<?> clazz) {\n        // TODO 暂时让能用先,但日志会显示Closing non transactional SqlSession,因为这个并没有绑定.\n        return sqlSessionFactory(clazz).openSession(ExecutorType.BATCH);\n    }\n\n    /**\n     * 获取SqlSessionFactory\n     *\n     * @param clazz 实体类\n     * @return SqlSessionFactory\n     * @since 3.3.0\n     * @deprecated 3.5.3.2 尽量少用,后期取消此方法获取实例\n     */\n    @Deprecated\n    public static SqlSessionFactory sqlSessionFactory(Class<?> clazz) {\n        return GlobalConfigUtils.currentSessionFactory(clazz);\n    }\n\n    /**\n     * 获取Session\n     *\n     * @param clazz 实体类\n     * @return SqlSession\n     * @deprecated 3.5.3.2 尽量少用,后期取消打开session方法\n     */\n    @Deprecated\n    public static SqlSession sqlSession(Class<?> clazz) {\n        return CompatibleHelper.getCompatibleSet().getSqlSession(GlobalConfigUtils.currentSessionFactory(clazz));\n    }\n\n    /**\n     * 获取TableInfo\n     *\n     * @param clazz 对象类\n     * @return TableInfo 对象表信息\n     */\n    public static TableInfo table(Class<?> clazz) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        Assert.notNull(tableInfo, \"Error: Cannot execute table Method, ClassGenericType not found.\");\n        return tableInfo;\n    }\n\n    /**\n     * 判断数据库操作是否成功\n     *\n     * @param result 数据库操作返回影响条数\n     * @return boolean\n     */\n    public static boolean retBool(Integer result) {\n        return null != result && result >= 1;\n    }\n\n    /**\n     * 判断数据库操作是否成功\n     *\n     * @param result 数据库操作返回影响条数\n     * @return boolean\n     */\n    public static boolean retBool(Long result) {\n        return null != result && result >= 1;\n    }\n\n    /**\n     * 批量操作是否成功\n     *\n     * @param result 批量操作结果集\n     * @return 操作结果(批量行记录全满足成功的的情况下为true)\n     * @since 3.5.8\n     */\n    public static boolean retBool(List<BatchResult> result) {\n        return result != null && result.stream().flatMapToInt(r -> IntStream.of(r.getUpdateCounts())).allMatch(i -> i > 0 || i == Statement.SUCCESS_NO_INFO);\n    }\n\n    /**\n     * 返回SelectCount执行结果\n     *\n     * @param result ignore\n     * @return int\n     */\n    public static long retCount(Long result) {\n        return (null == result) ? 0 : result;\n    }\n\n    /**\n     * 从list中取第一条数据返回对应List中泛型的单个结果\n     *\n     * @param list ignore\n     * @param <E>  ignore\n     * @return ignore\n     */\n    public static <E> E getObject(Log log, List<E> list) {\n        return getObject(() -> log, list);\n    }\n\n    /**\n     * @since 3.4.3\n     */\n    public static <E> E getObject(Supplier<Log> supplier, List<E> list) {\n        if (CollectionUtils.isNotEmpty(list)) {\n            int size = list.size();\n            if (size > 1) {\n                Log log = supplier.get();\n                log.warn(String.format(\"Warn: execute Method There are  %s results.\", size));\n            }\n            return list.get(0);\n        }\n        return null;\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param entityClass 实体\n     * @param log         日志对象\n     * @param consumer    consumer\n     * @return 操作结果\n     * @since 3.4.0\n     */\n    @Deprecated\n    public static boolean executeBatch(Class<?> entityClass, Log log, Consumer<SqlSession> consumer) {\n        return executeBatch(sqlSessionFactory(entityClass), log, consumer);\n    }\n\n    public static boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Consumer<SqlSession> consumer) {\n        return CompatibleHelper.getCompatibleSet().executeBatch(sqlSessionFactory, log, consumer);\n    }\n\n    /**\n     * 执行批量操作\n     *\n     * @param entityClass 实体类\n     * @param log         日志对象\n     * @param list        数据集合\n     * @param batchSize   批次大小\n     * @param consumer    consumer\n     * @param <E>         T\n     * @return 操作结果\n     * @since 3.4.0\n     * @deprecated {@link #executeBatch(SqlSessionFactory, Log, Collection, int, BiConsumer)}\n     */\n    @Deprecated\n    public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {\n        return executeBatch(sqlSessionFactory(entityClass), log, list, batchSize, consumer);\n    }\n\n    public static <E> boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {\n        Assert.isFalse(batchSize < 1, \"batchSize must not be less than one\");\n        return !CollectionUtils.isEmpty(list) && executeBatch(sqlSessionFactory, log, sqlSession -> {\n            int size = list.size();\n            int idxLimit = Math.min(batchSize, size);\n            int i = 1;\n            for (E element : list) {\n                consumer.accept(sqlSession, element);\n                if (i == idxLimit) {\n                    sqlSession.flushStatements();\n                    idxLimit = Math.min(idxLimit + batchSize, size);\n                }\n                i++;\n            }\n        });\n    }\n\n    /**\n     * 批量更新或保存\n     *\n     * @param entityClass 实体\n     * @param log         日志对象\n     * @param list        数据集合\n     * @param batchSize   批次大小\n     * @param predicate   predicate(新增条件) notNull\n     * @param consumer    consumer（更新处理） notNull\n     * @param <E>         E\n     * @return 操作结果\n     * @since 3.4.0\n     * @deprecated 3.5.4 {@link #saveOrUpdateBatch(SqlSessionFactory, Class, Log, Collection, int, BiPredicate, BiConsumer)}\n     */\n    @Deprecated\n    public static <E> boolean saveOrUpdateBatch(Class<?> entityClass, Class<?> mapper, Log log, Collection<E> list, int batchSize, BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer) {\n        return saveOrUpdateBatch(sqlSessionFactory(entityClass), mapper, log, list, batchSize, predicate, consumer);\n    }\n\n    /**\n     * 批量更新或保存\n     *\n     * @param sqlSessionFactory SqlSessionFactory\n     * @param log               日志对象\n     * @param list              数据集合\n     * @param batchSize         批次大小\n     * @param predicate         predicate(新增条件) notNull\n     * @param consumer          consumer（更新处理） notNull\n     * @param <E>               E\n     * @return 操作结果\n     * @since 3.5.4\n     */\n    public static <E> boolean saveOrUpdateBatch(SqlSessionFactory sqlSessionFactory, Class<?> mapper, Log log, Collection<E> list, int batchSize, BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer) {\n        String sqlStatement = getSqlStatement(mapper, SqlMethod.INSERT_ONE);\n        return executeBatch(sqlSessionFactory, log, list, batchSize, (sqlSession, entity) -> {\n            if (predicate.test(sqlSession, entity)) {\n                sqlSession.insert(sqlStatement, entity);\n            } else {\n                consumer.accept(sqlSession, entity);\n            }\n        });\n    }\n\n    /**\n     * 获取mapperStatementId\n     *\n     * @param sqlMethod 方法名\n     * @return 命名id\n     * @since 3.4.0\n     */\n    public static String getSqlStatement(Class<?> mapper, SqlMethod sqlMethod) {\n        return mapper.getName() + StringPool.DOT + sqlMethod.getMethod();\n    }\n\n    /**\n     * 通过entityClass获取Mapper，记得要释放连接\n     * 例： {@code\n     * SqlSession sqlSession = SqlHelper.sqlSession(entityClass);\n     * try {\n     * BaseMapper<User> userMapper = getMapper(User.class, sqlSession);\n     * } finally {\n     * sqlSession.close();\n     * }\n     * }\n     *\n     * @param entityClass 实体\n     * @param <T>         实体类型\n     * @param <M>         Mapper类型\n     * @return Mapper\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T, M extends BaseMapper<T>> M getMapper(Class<T> entityClass, SqlSession sqlSession) {\n        Assert.notNull(entityClass, \"entityClass can't be null!\");\n        TableInfo tableInfo = Optional.ofNullable(TableInfoHelper.getTableInfo(entityClass)).orElseThrow(() -> ExceptionUtils.mpe(\"Can not find TableInfo from Class: \\\"%s\\\".\", entityClass.getName()));\n        Class<?> mapperClass = ClassUtils.toClassConfident(tableInfo.getCurrentNamespace());\n        if (CompatibleHelper.hasCompatibleSet()) {\n            CompatibleSet compatibleSet = CompatibleHelper.getCompatibleSet();\n            Object bean = compatibleSet.getBean(mapperClass);\n            if (bean != null) {\n                return (M) bean;\n            }\n        }\n        return (M) tableInfo.getConfiguration().getMapper(mapperClass, sqlSession);\n    }\n\n    /**\n     * 通过entityClass获取BaseMapper，再传入lambda使用该mapper，本方法自动释放链接\n     *\n     * @param entityClass 实体类\n     * @param sFunction   lambda操作，例如 {@code m->m.selectList(wrapper)}\n     * @param <T>         实体类的类型\n     * @param <R>         返回值类型\n     * @param <M>         Mapper类型\n     * @return 返回lambda执行结果\n     */\n    public static <T, R, M extends BaseMapper<T>> R execute(Class<T> entityClass, SFunction<M, R> sFunction) {\n        SqlSession sqlSession = SqlHelper.sqlSession(entityClass);\n        try {\n            return sFunction.apply(SqlHelper.getMapper(entityClass, sqlSession));\n        } finally {\n            CompatibleHelper.getCompatibleSet().closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(entityClass));\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SqlParserUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\n/**\n * SQL 解析工具类\n *\n * @author hubin\n * @since 2018-07-22\n */\npublic class SqlParserUtils {\n\n    /**\n     * 获取 COUNT 原生 SQL 包装\n     *\n     * @param originalSql ignore\n     * @return ignore\n     */\n    public static String getOriginalCountSql(String originalSql) {\n        return String.format(\"SELECT COUNT(*) FROM (%s) TOTAL\", originalSql);\n    }\n\n    /**\n     * 去除表或字段包裹符号\n     *\n     * @param tableOrColumn 表名或字段名\n     * @return str\n     * @since 3.5.6\n     */\n    public static String removeWrapperSymbol(String tableOrColumn) {\n        if (StringUtils.isBlank(tableOrColumn)) {\n            return null;\n        }\n        if (tableOrColumn.startsWith(\"`\") || tableOrColumn.startsWith(\"\\\"\")\n            || tableOrColumn.startsWith(\"[\") || tableOrColumn.startsWith(\"<\")) {\n            return tableOrColumn.substring(1, tableOrColumn.length() - 1);\n        }\n        return tableOrColumn;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 工具类\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/kotlin/com/baomidou/mybatisplus/extension/kotlin/AbstractKtWrapper.kt",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.kotlin\n\nimport com.baomidou.mybatisplus.core.conditions.AbstractWrapper\nimport com.baomidou.mybatisplus.core.toolkit.LambdaUtils\nimport com.baomidou.mybatisplus.core.toolkit.StringPool\nimport com.baomidou.mybatisplus.core.toolkit.support.ColumnCache\nimport java.util.stream.Collectors.joining\nimport kotlin.reflect.KProperty1\n\n/**\n * Lambda 语法使用 Wrapper\n *\n * 统一处理解析 lambda 获取 column\n *\n * @author yangyuhan,MieMie,HanChunLin\n * @since 2018-11-07\n */\n@Suppress(\"serial\")\nabstract class AbstractKtWrapper<T, Children : AbstractKtWrapper<T, Children>> : AbstractWrapper<T, KProperty1<in T, *>, Children>() {\n\n    /**\n     * 列 Map\n     */\n    protected lateinit var columnMap: Map<String, ColumnCache>\n\n    /**\n     * 重载方法，默认 onlyColumn = true\n     */\n    override fun columnToString(kProperty: KProperty1<in T, *>): String? = columnToString(kProperty, true)\n\n    /**\n     * 核心实现方法，供重载使用\n     */\n    private fun columnToString(kProperty: KProperty1<in T, *>, onlyColumn: Boolean): String? {\n        return columnMap[LambdaUtils.formatKey(kProperty.name)]?.let { if (onlyColumn) it.column else it.columnSelect }\n    }\n\n    /**\n     * 批量处理传入的属性，正常情况下的输出就像：\n     *\n     * \"user_id\" AS \"userId\" , \"user_name\" AS \"userName\"\n     */\n    fun columnsToString(onlyColumn: Boolean, vararg columns: KProperty1<in T, *>): String =\n        columns.mapNotNull { columnToString(it, onlyColumn) }.joinToString(separator = StringPool.COMMA)\n\n    /**\n     * 批量处理传入的属性，正常情况下的输出就像：\n     *\n     * \"user_id\" AS \"userId\" , \"user_name\" AS \"userName\"\n     */\n    fun columnsToString(onlyColumn: Boolean, columns: MutableList<KProperty1<in T, *>>): String =\n        columns.stream().map { columnToString(it, onlyColumn) }.collect(joining(StringPool.COMMA))\n\n    override fun initNeed() {\n        super.initNeed()\n        if (!::columnMap.isInitialized) {\n            columnMap = LambdaUtils.getColumnMap(this.entityClass)\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/kotlin/com/baomidou/mybatisplus/extension/kotlin/KtQueryChainWrapper.kt",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.kotlin\n\nimport com.baomidou.mybatisplus.core.conditions.query.Query\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper\nimport com.baomidou.mybatisplus.extension.conditions.query.ChainQuery\nimport java.util.function.Predicate\nimport kotlin.reflect.KProperty1\n\n/**\n * @author FlyInWind\n * @since 2020-10-18\n */\n@Suppress(\"serial\")\nopen class KtQueryChainWrapper<T : Any>(\n    internal val baseMapper: BaseMapper<T>?\n) : AbstractChainWrapper<T, KProperty1<in T, *>, KtQueryChainWrapper<T>, KtQueryWrapper<T>>(),\n    ChainQuery<T>, Query<KtQueryChainWrapper<T>, T, KProperty1<in T, *>> {\n\n    constructor(baseMapper: BaseMapper<T>, entityClass: Class<T>) : this(baseMapper) {\n        super.wrapperChildren = KtQueryWrapper(entityClass)\n    }\n\n    constructor(baseMapper: BaseMapper<T>, entity: T) : this(baseMapper) {\n        super.wrapperChildren = KtQueryWrapper(entity)\n    }\n\n    constructor(entityClass: Class<T>) : this(null) {\n        super.wrapperChildren = KtQueryWrapper(entityClass)\n    }\n\n    constructor(entity: T) : this(null) {\n        super.wrapperChildren = KtQueryWrapper(entity)\n        super.setEntityClass(entity.javaClass)\n    }\n\n    override fun select(condition: Boolean, columns: MutableList<KProperty1<in T, *>>): KtQueryChainWrapper<T> {\n        wrapperChildren.select(condition, columns)\n        return typedThis\n    }\n\n    override fun select(predicate: Predicate<TableFieldInfo>): KtQueryChainWrapper<T> {\n        return select(entityClass, predicate)\n    }\n\n    override fun select(entityClass: Class<T>, predicate: Predicate<TableFieldInfo>): KtQueryChainWrapper<T> {\n        wrapperChildren.select(entityClass, predicate)\n        return typedThis\n    }\n\n    override fun getBaseMapper(): BaseMapper<T>? {\n        return baseMapper\n    }\n\n    override fun getEntityClass(): Class<T> {\n        return super.wrapperChildren.entityClass\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/kotlin/com/baomidou/mybatisplus/extension/kotlin/KtQueryWrapper.kt",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.kotlin\n\nimport com.baomidou.mybatisplus.core.conditions.SharedString\nimport com.baomidou.mybatisplus.core.conditions.query.Query\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils\nimport com.baomidou.mybatisplus.core.toolkit.support.ColumnCache\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.function.Predicate\nimport kotlin.reflect.KProperty1\n\n/**\n * Kotlin Lambda 语法使用 Wrapper\n *\n * @author yangyuhan\n * @since 2018-11-02\n */\n@Suppress(\"serial\")\nopen class KtQueryWrapper<T : Any> : AbstractKtWrapper<T, KtQueryWrapper<T>>, Query<KtQueryWrapper<T>, T, KProperty1<in T, *>> {\n\n    /**\n     * 查询字段\n     */\n    private var sqlSelect: SharedString = SharedString()\n\n    constructor(entity: T) {\n        this.entity = entity\n        super.initNeed()\n    }\n\n    constructor(entityClass: Class<T>) {\n        this.entityClass = entityClass\n        super.initNeed()\n    }\n\n    internal constructor(entity: T?, entityClass: Class<T>, sqlSelect: SharedString, paramNameSeq: AtomicInteger,\n                         paramNameValuePairs: Map<String, Any>, columnMap: Map<String, ColumnCache>,\n                         lastSql: SharedString, sqlComment: SharedString, sqlFirst: SharedString) {\n        this.entity = entity\n        this.paramNameSeq = paramNameSeq\n        this.paramNameValuePairs = paramNameValuePairs\n        this.expression = MergeSegments()\n        this.columnMap = columnMap\n        this.sqlSelect = sqlSelect\n        this.entityClass = entityClass\n        this.lastSql = lastSql\n        this.sqlComment = sqlComment\n        this.sqlFirst = sqlFirst\n    }\n\n    override fun select(condition: Boolean, columns: MutableList<KProperty1<in T, *>>): KtQueryWrapper<T> {\n        if (condition && CollectionUtils.isNotEmpty(columns)) {\n            this.sqlSelect.stringValue = columnsToString(false, columns)\n        }\n        return typedThis\n    }\n\n    override fun select(predicate: Predicate<TableFieldInfo>): KtQueryWrapper<T> {\n        return select(entityClass, predicate) as KtQueryWrapper<T>\n    }\n\n    override fun select(entityClass: Class<T>, predicate: Predicate<TableFieldInfo>): KtQueryWrapper<T> {\n        this.entityClass = entityClass\n        this.sqlSelect.stringValue = TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate)\n        return typedThis\n    }\n\n    override fun getSqlSelect(): String? {\n        return sqlSelect.stringValue\n    }\n\n    /**\n     * 用于生成嵌套 sql\n     *\n     * 故 sqlSelect 不向下传递\n     */\n    override fun instance(): KtQueryWrapper<T> {\n        return KtQueryWrapper(entity, entityClass, sqlSelect, paramNameSeq, paramNameValuePairs, columnMap,\n            SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString())\n    }\n\n    override fun clear() {\n        super.clear()\n        sqlSelect.toNull()\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/kotlin/com/baomidou/mybatisplus/extension/kotlin/KtUpdateChainWrapper.kt",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.kotlin\n\nimport com.baomidou.mybatisplus.core.conditions.update.Update\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper\nimport com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper\nimport com.baomidou.mybatisplus.extension.conditions.update.ChainUpdate\nimport kotlin.reflect.KProperty1\n\n/**\n * @author FlyInWind\n * @since 2020-10-18\n */\n@Suppress(\"serial\")\nopen class KtUpdateChainWrapper<T : Any>(\n    internal val baseMapper: BaseMapper<T>?\n) : AbstractChainWrapper<T, KProperty1<in T, *>, KtUpdateChainWrapper<T>, KtUpdateWrapper<T>>(),\n    ChainUpdate<T>, Update<KtUpdateChainWrapper<T>, KProperty1<in T, *>> {\n\n    constructor(baseMapper: BaseMapper<T>, entityClass: Class<T>) : this(baseMapper) {\n        super.wrapperChildren = KtUpdateWrapper(entityClass)\n    }\n\n    constructor(baseMapper: BaseMapper<T>, entity: T) : this(baseMapper) {\n        super.wrapperChildren = KtUpdateWrapper(entity)\n    }\n\n    constructor(entityClass: Class<T>) : this(null) {\n        super.wrapperChildren = KtUpdateWrapper(entityClass)\n    }\n\n    constructor(entity: T) : this(null) {\n        super.wrapperChildren = KtUpdateWrapper(entity)\n        super.setEntityClass(entity.javaClass)\n    }\n\n    override fun set(condition: Boolean, column: KProperty1<in T, *>, value: Any?, mapping: String?): KtUpdateChainWrapper<T> {\n        wrapperChildren.set(condition, column, value, mapping)\n        return typedThis\n    }\n\n    override fun setSql(condition: Boolean, setSql: String, vararg params: Any): KtUpdateChainWrapper<T> {\n        wrapperChildren.setSql(condition, setSql, *params)\n        return typedThis\n    }\n\n    override fun setDecrBy(condition: Boolean, column: KProperty1<in T, *>, `val`: Number): KtUpdateChainWrapper<T> {\n        wrapperChildren.setDecrBy(condition, column, `val`)\n        return typedThis\n    }\n\n    override fun setIncrBy(condition: Boolean, column: KProperty1<in T, *>, `val`: Number): KtUpdateChainWrapper<T> {\n        wrapperChildren.setIncrBy(condition, column, `val`)\n        return typedThis\n    }\n\n    override fun getBaseMapper(): BaseMapper<T>? {\n        return baseMapper\n    }\n\n    override fun getEntityClass(): Class<T> {\n        return super.wrapperChildren.entityClass\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/main/kotlin/com/baomidou/mybatisplus/extension/kotlin/KtUpdateWrapper.kt",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.kotlin\n\nimport com.baomidou.mybatisplus.core.conditions.SharedString\nimport com.baomidou.mybatisplus.core.conditions.segments.MergeSegments\nimport com.baomidou.mybatisplus.core.conditions.update.Update\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils\nimport com.baomidou.mybatisplus.core.toolkit.Constants\nimport com.baomidou.mybatisplus.core.toolkit.StringPool\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils\nimport com.baomidou.mybatisplus.core.toolkit.support.ColumnCache\nimport java.math.BigDecimal\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.stream.Collectors.joining\nimport kotlin.reflect.KProperty1\n\n/**\n * Kotlin Lambda 更新封装\n *\n * @author yangyuhan\n * @since 2018-11-02\n */\n@Suppress(\"serial\")\nopen class KtUpdateWrapper<T : Any> : AbstractKtWrapper<T, KtUpdateWrapper<T>>, Update<KtUpdateWrapper<T>, KProperty1<in T, *>> {\n\n    /**\n     * SQL 更新字段内容，例如：name='1', age=2\n     */\n    private val sqlSet = ArrayList<String>()\n\n    constructor(entity: T) {\n        this.entity = entity\n        super.initNeed()\n    }\n\n    constructor(entityClass: Class<T>) {\n        this.entityClass = entityClass\n        super.initNeed()\n    }\n\n    internal constructor(entity: T?, paramNameSeq: AtomicInteger, paramNameValuePairs: Map<String, Any>,\n                         columnMap: Map<String, ColumnCache>, lastSql: SharedString, sqlComment: SharedString,\n                         sqlFirst: SharedString) {\n        this.entity = entity\n        this.paramNameSeq = paramNameSeq\n        this.paramNameValuePairs = paramNameValuePairs\n        this.expression = MergeSegments()\n        this.columnMap = columnMap\n        this.lastSql = lastSql\n        this.sqlComment = sqlComment\n        this.sqlFirst = sqlFirst\n    }\n\n    override fun getSqlSet(): String? {\n        return if (CollectionUtils.isEmpty(sqlSet)) null\n        else sqlSet.stream().collect(joining(StringPool.COMMA))\n    }\n\n    override fun set(condition: Boolean, column: KProperty1<in T, *>, value: Any?, mapping: String?): KtUpdateWrapper<T> {\n        return maybeDo(condition) {\n            val sql = formatParam(mapping, value)\n            sqlSet.add(columnsToString(column) + Constants.EQUALS + sql)\n        }\n    }\n\n    override fun setSql(condition: Boolean, setSql: String, vararg params: Any): KtUpdateWrapper<T> {\n        return maybeDo(condition && StringUtils.isNotBlank(setSql)) {\n            sqlSet.add(formatSqlMaybeWithParam(setSql, *params))\n        }\n    }\n\n    override fun setIncrBy(condition: Boolean, column: KProperty1<in T, *>, `val`: Number): KtUpdateWrapper<T> {\n        return maybeDo(condition) {\n            val realColumn = columnToString(column)\n            sqlSet.add(String.format(\"%s=%s + %s\", realColumn, realColumn, if (`val` is BigDecimal) `val`.toPlainString() else `val`))\n        }\n    }\n\n    override fun setDecrBy(condition: Boolean, column: KProperty1<in T, *>, `val`: Number): KtUpdateWrapper<T> {\n        return maybeDo(condition) {\n            val realColumn = columnToString(column)\n            sqlSet.add(String.format(\"%s=%s - %s\", realColumn, realColumn, if (`val` is BigDecimal) `val`.toPlainString() else `val`));\n        }\n    }\n\n    override fun instance(): KtUpdateWrapper<T> {\n        return KtUpdateWrapper(entity, paramNameSeq, paramNameValuePairs, columnMap,\n            SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString())\n    }\n\n    override fun clear() {\n        super.clear()\n        sqlSet.clear()\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/DdlHelperTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.extension.ddl.DdlHelper;\nimport com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler;\nimport com.baomidou.mybatisplus.extension.ddl.history.IDdlGenerator;\nimport com.baomidou.mybatisplus.extension.ddl.history.MysqlDdlGenerator;\nimport com.baomidou.mybatisplus.extension.ddl.history.OracleDdlGenerator;\nimport com.baomidou.mybatisplus.extension.ddl.history.PostgreDdlGenerator;\nimport com.baomidou.mybatisplus.extension.ddl.history.SQLiteDdlGenerator;\nimport org.apache.ibatis.datasource.unpooled.UnpooledDataSource;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nimport java.sql.SQLException;\nimport java.util.List;\n\n/**\n *\n * @author nieqiurong\n */\npublic class DdlHelperTest {\n\n    @Test\n    @Disabled\n    void testForMysql() throws SQLException {\n        var dataSource = new UnpooledDataSource(com.mysql.cj.jdbc.Driver.class.getName(),\n            \"jdbc:mysql://127.0.0.1:3306/baomidou?serverTimezone=Asia/Shanghai\",\n            \"root\", \"123456\");\n        var ddlGenerator = new MysqlDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    @Disabled\n    void testForPostgresql() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),\n            \"jdbc:postgresql://localhost:5432/postgres\",\n            \"postgres\", \"123456\");\n        IDdlGenerator ddlGenerator = PostgreDdlGenerator.newInstance();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n        // 指定scheme运行 旧版本的模式是指定 public,为了兼容当使用默认的示例是无法根据指定的模式走的\n        dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),\n            \"jdbc:postgresql://localhost:5432/postgres?currentSchema=baomidou\",\n            \"postgres\", \"123456\");\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    @Disabled\n    void testForPostgresqlForAuto() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),\n            \"jdbc:postgresql://localhost:5432/postgres\",\n            \"postgres\", \"123456\");\n        IDdlGenerator ddlGenerator = PostgreDdlGenerator.newInstanceWithAutoSchema();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n\n        // 指定scheme运行\n        dataSource = new UnpooledDataSource(org.postgresql.Driver.class.getName(),\n            \"jdbc:postgresql://localhost:5432/postgres?currentSchema=baomidou\",\n            \"postgres\", \"123456\");\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n\n    }\n\n    @Test\n    @Disabled\n    void testForOracle() throws SQLException {\n        var dataSource = new UnpooledDataSource(oracle.jdbc.driver.OracleDriver.class.getName(),\n            \"jdbc:oracle:thin:@127.0.0.1:1521:orcl\",\n            \"system\", \"123456\");\n        var ddlGenerator = new OracleDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    void testForSQLite() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.sqlite.JDBC.class.getName(),\n            \"jdbc:sqlite:test.db\",\n            \"\", \"\");\n        var ddlGenerator = new SQLiteDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    void testForH2() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.h2.Driver.class.getName(),\n            \"jdbc:h2:mem:test;DATABASE_TO_LOWER=TRUE\",\n            \"sa\", \"\");\n        DdlHelper.runScript(null, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    void testForH2Mysql() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.h2.Driver.class.getName(),\n            \"jdbc:h2:mem:test;MODE=MySQL\",\n            \"sa\", \"\");\n        var ddlGenerator = new MysqlDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    void testForH2Postgresql() throws SQLException {\n        var dataSource = new UnpooledDataSource(org.h2.Driver.class.getName(),\n            \"jdbc:h2:mem:test;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE\",\n            \"sa\", \"\");\n        var ddlGenerator = new PostgreDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n    @Test\n    @Disabled\n    void testForDm() throws SQLException {\n        var dataSource = new UnpooledDataSource(dm.jdbc.driver.DmDriver.class.getName(),\n            \"jdbc:dm://127.0.0.1:5236/DMSERVER\",\n            \"SYSDBA\", \"Dm123456\");\n        var ddlGenerator = new OracleDdlGenerator();\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n        // 指定模式运行\n        ddlGenerator = new OracleDdlGenerator(\"TEST1\");\n        DdlHelper.runScript(ddlGenerator, dataSource, List.of(\"ddl/test.sql\"),\n            true, DdlScriptErrorHandler.ThrowsErrorHandler.INSTANCE);\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/DdlScriptTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.extension.ddl.DdlScript;\nimport org.h2.Driver;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DdlScriptTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(DdlScriptTest.class);\n\n    @Test\n    void test() throws Exception {\n        var ddlScript = new DdlScript(Driver.class.getName(),\n            \"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\",\n            \"sa\", \"\", true).scriptRunner(scriptRunner -> {\n                scriptRunner.setLogWriter(null);\n        });\n        LOGGER.info(\"--------------execute----------------\");\n        ddlScript.run( \"select 1 from dual;\", msg ->{});\n        LOGGER.info(\"--------------execute----------------\");\n        ddlScript.run( \"select 2 from dual;\", msg ->{});\n        LOGGER.info(\"--------------run----------------\");\n        ddlScript.run(\"select 1 from dual;\");\n        LOGGER.info(\"--------------run----------------\");\n        ddlScript.run(\"select 3 from dual#\", \"#\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/SqlParserUtilsTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\npublic class SqlParserUtilsTest {\n\n    @Test\n    void testRemoveWrapperSymbol() {\n        //用SQLServer的人喜欢写这种\n        Assertions.assertEquals(\"Demo\", SqlParserUtils.removeWrapperSymbol(\"[Demo]\"));\n        Assertions.assertEquals(\"Demo\", SqlParserUtils.removeWrapperSymbol(\"Demo\"));\n        //mysql比较常见\n        Assertions.assertEquals(\"Demo\", SqlParserUtils.removeWrapperSymbol(\"`Demo`\"));\n        //用关键字表的\n        Assertions.assertEquals(\"Demo\", SqlParserUtils.removeWrapperSymbol(\"\\\"Demo\\\"\"));\n        //这种少\n        Assertions.assertEquals(\"Demo\", SqlParserUtils.removeWrapperSymbol(\"<Demo>\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/parsers/DynamicTableNameParserTest.java",
    "content": "//package com.baomidou.mybatisplus.extension.parsers;\n//\n//import org.junit.jupiter.api.Test;\n//\n//import java.util.HashMap;\n//import java.util.Map;\n//\n//import static org.junit.jupiter.api.Assertions.assertEquals;\n//\n///**\n// * Create by hcl at 2020/6/30\n// */\n//class DynamicTableNameParserTest {\n//\n//    @Test\n//    void parser() {\n//        DynamicTableNameParser parser = new DynamicTableNameParser();\n//        Map<String, ITableNameHandler> tableNameHandlerMap = new HashMap<>();\n//        tableNameHandlerMap.put(\"t_user\",\n//                // https://github.com/baomidou/mybatis-plus/issues/2411\n//                (metaObject, sql, tableName) -> {\n//                    if (\"t_user\".equals(tableName)) {\n//                        return \"t_user_2019\";\n//                    }\n//                    return tableName;\n//                });\n//        parser.setTableNameHandlerMap(tableNameHandlerMap);\n//\n//        String before = \"select a.* from t_user a join t_userrole b on b.userid = a.gid\";\n//        String after = \"select a.* from t_user_2019 a join t_userrole b on b.userid = a.gid\";\n//        assertEquals(after, parser.parser(null, before).getSql());\n//\n//        before = \"select * from t_user\";\n//        after = \"select * from t_user_2019\";\n//        assertEquals(after, parser.parser(null, before).getSql());\n//\n//        before = \"insert into t_user(id,name) values('1','zhangsan')\";\n//        after = \"insert into t_user_2019(id,name) values('1','zhangsan')\";\n//        assertEquals(after, parser.parser(null, before).getSql());\n//        before = \"select a.*,\\n\" +\n//                \"        (select count(1) from t_user) as cnt\\n\" +\n//                \"        from t_xxx a\";\n//        after = \"select a.*,\\n\" +\n//                \"        (select count(1) from t_user_2019) as cnt\\n\" +\n//                \"        from t_xxx a\";\n//        assertEquals(after, parser.parser(null, before).getSql());\n//    }\n//\n//}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/pagination/dialects/IDialectTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.pagination.dialects;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;\nimport lombok.extern.slf4j.Slf4j;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2022-05-30\n */\n@Slf4j\nclass IDialectTest {\n\n    /**\n     * 检查 IDialect 的实现类生成的sql不要老是有重复,同样实现类的sql类型只需要留一个\n     */\n    @Test\n    void singleRealize() {\n        List<Class<?>> classList = ReflectionUtils.findAllClassesInPackage(\n            \"com.baomidou.mybatisplus.extension.plugins.pagination.dialects\",\n            i -> !i.isInterface() && IDialect.class.isAssignableFrom(i), i -> true);\n\n        Map<String, List<Class<?>>> map = new ConcurrentHashMap<>();\n        classList.forEach(i -> {\n            IDialect o = (IDialect) ReflectionUtils.newInstance(i);\n            DialectModel model = o.buildPaginationSql(\"select * from table\", 1, 10);\n            String sql = model.getDialectSql();\n            if (!map.containsKey(sql)) {\n                ArrayList<Class<?>> list = new ArrayList<>();\n                list.add(i);\n                map.put(sql,list);\n            } else {\n                map.get(sql).add(i);\n            }\n        });\n        map.forEach((k, v) -> {\n            List<Class<?>> list = v.stream().filter(i -> i.getAnnotation(Deprecated.class) == null).toList();\n            if(list.size() > 1){\n                log.warn(\"{}的sql重复\", list.stream().map(Class::getSimpleName).collect(Collectors.joining(\"与\")));\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/BaseTypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport org.mockito.Mock;\n\nimport java.sql.CallableStatement;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\n\npublic abstract class BaseTypeHandlerTest {\n\n    @Mock\n    protected ResultSet resultSet;\n    @Mock\n    protected PreparedStatement preparedStatement;\n    @Mock\n    protected CallableStatement callableStatement;\n\n    public abstract void setParameter() throws Exception;\n\n    public abstract void getResultFromResultSetByColumnName() throws Exception;\n\n    public abstract void getResultFromResultSetByColumnIndex() throws Exception;\n\n    public abstract void getResultFromCallableStatement() throws Exception;\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/FastJson2TypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler;\nimport com.baomidou.mybatisplus.test.model.UserBean;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.lenient;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@ExtendWith(MockitoExtension.class)\npublic class FastJson2TypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final Fastjson2TypeHandler FASTJSON_TYPE_HANDLER = new Fastjson2TypeHandler(UserBean.class);\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 1, null, JdbcType.VARCHAR);\n        verify(preparedStatement).setNull(1, JdbcType.VARCHAR.TYPE_CODE);\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 2, \"\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(2, \"\\\"\\\"\");\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 3, \"{}\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(3, \"\\\"{}\\\"\");\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(\"column\")).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(1)).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(2)).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 2);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        UserBean bean;\n        lenient().when(callableStatement.getString(1)).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(2)).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(callableStatement, 2);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(callableStatement, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/FastJsonTypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;\nimport com.baomidou.mybatisplus.test.model.UserBean;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@ExtendWith(MockitoExtension.class)\npublic class FastJsonTypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final FastjsonTypeHandler FASTJSON_TYPE_HANDLER = new FastjsonTypeHandler(UserBean.class);\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 1, null, JdbcType.VARCHAR);\n        verify(preparedStatement).setNull(1, JdbcType.VARCHAR.TYPE_CODE);\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 2, \"\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(2, \"\\\"\\\"\");\n        FASTJSON_TYPE_HANDLER.setParameter(preparedStatement, 3, \"{}\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(3, \"\\\"{}\\\"\");\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(\"column\")).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(1)).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(2)).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 2);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        UserBean bean;\n        lenient().when(callableStatement.getString(1)).thenReturn(null);\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(2)).thenReturn(\"\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(callableStatement, 2);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) FASTJSON_TYPE_HANDLER.getResult(callableStatement, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/GsonTypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;\nimport com.baomidou.mybatisplus.test.model.UserBean;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@ExtendWith(MockitoExtension.class)\npublic class GsonTypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final GsonTypeHandler GSON_TYPE_HANDLER = new GsonTypeHandler(UserBean.class);\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        GSON_TYPE_HANDLER.setParameter(preparedStatement, 1, null, JdbcType.VARCHAR);\n        verify(preparedStatement).setNull(1, JdbcType.VARCHAR.TYPE_CODE);\n        GSON_TYPE_HANDLER.setParameter(preparedStatement, 2, \"\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(2, \"\\\"\\\"\");\n        GSON_TYPE_HANDLER.setParameter(preparedStatement, 3, \"{}\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(3, \"\\\"{}\\\"\");\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(\"column\")).thenReturn(null);\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(1)).thenReturn(null);\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(2)).thenReturn(\"\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, 2);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        UserBean bean;\n        lenient().when(callableStatement.getString(1)).thenReturn(null);\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(2)).thenReturn(\"\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(callableStatement, 2);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) GSON_TYPE_HANDLER.getResult(callableStatement, 3);\n        assertEquals(bean.getId(), 123L);\n        assertEquals(bean.getName(), \"测试\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/Jackson3TypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler;\nimport com.baomidou.mybatisplus.test.model.UserBean;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.lenient;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@ExtendWith(MockitoExtension.class)\npublic class Jackson3TypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final Jackson3TypeHandler JACKSON3_TYPE_HANDLER = new Jackson3TypeHandler(UserBean.class);\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        JACKSON3_TYPE_HANDLER.setParameter(preparedStatement, 1, null, JdbcType.VARCHAR);\n        verify(preparedStatement).setNull(1, JdbcType.VARCHAR.TYPE_CODE);\n        JACKSON3_TYPE_HANDLER.setParameter(preparedStatement, 2, \"\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(2, \"\\\"\\\"\");\n        JACKSON3_TYPE_HANDLER.setParameter(preparedStatement, 3, \"{}\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(3, \"\\\"{}\\\"\");\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(\"column\")).thenReturn(null);\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, \"column\");\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(1)).thenReturn(null);\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(2)).thenReturn(\"\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, 2);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        UserBean bean;\n        lenient().when(callableStatement.getString(1)).thenReturn(null);\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(2)).thenReturn(\"\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(callableStatement, 2);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON3_TYPE_HANDLER.getResult(callableStatement, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/JacksonTypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport com.baomidou.mybatisplus.test.model.UserBean;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@ExtendWith(MockitoExtension.class)\npublic class JacksonTypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final JacksonTypeHandler JACKSON_TYPE_HANDLER = new JacksonTypeHandler(UserBean.class);\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        JACKSON_TYPE_HANDLER.setParameter(preparedStatement, 1, null, JdbcType.VARCHAR);\n        verify(preparedStatement).setNull(1, JdbcType.VARCHAR.TYPE_CODE);\n        JACKSON_TYPE_HANDLER.setParameter(preparedStatement, 2, \"\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(2, \"\\\"\\\"\");\n        JACKSON_TYPE_HANDLER.setParameter(preparedStatement, 3, \"{}\", JdbcType.VARCHAR);\n        verify(preparedStatement).setString(3, \"\\\"{}\\\"\");\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(\"column\")).thenReturn(null);\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        Assertions.assertNull(bean);\n        when(resultSet.getString(\"column\")).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, \"column\");\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        UserBean bean;\n        when(resultSet.getString(1)).thenReturn(null);\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(2)).thenReturn(\"\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, 2);\n        Assertions.assertNull(bean);\n        when(resultSet.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        UserBean bean;\n        lenient().when(callableStatement.getString(1)).thenReturn(null);\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(resultSet, 1);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(2)).thenReturn(\"\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(callableStatement, 2);\n        Assertions.assertNull(bean);\n        when(callableStatement.getString(3)).thenReturn(\"{\\\"id\\\":123,\\\"name\\\":\\\"测试\\\"}\");\n        bean = (UserBean) JACKSON_TYPE_HANDLER.getResult(callableStatement, 3);\n        assertEquals(123L, bean.getId());\n        assertEquals(\"测试\", bean.getName());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/MybatisEnumTypeHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.annotation.EnumValue;\nimport com.baomidou.mybatisplus.annotation.IEnum;\nimport com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\npublic class MybatisEnumTypeHandlerTest extends BaseTypeHandlerTest {\n\n    private static final MybatisEnumTypeHandler<SexEnum> SEX_ENUM_ENUM_TYPE_HANDLER = new MybatisEnumTypeHandler<>(SexEnum.class);\n\n    private static final MybatisEnumTypeHandler<GradeEnum> GRADE_ENUM_ENUM_TYPE_HANDLER = new MybatisEnumTypeHandler<>(GradeEnum.class);\n\n    private static final MybatisEnumTypeHandler<CharacterEnum> CHARACTER_ENUM_MYBATIS_ENUM_TYPE_HANDLER = new MybatisEnumTypeHandler<>(CharacterEnum.class);\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnName() throws Exception {\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(null);\n        assertNull(SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(1);\n        assertEquals(SexEnum.MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(2);\n        assertEquals(SexEnum.WO_MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(null);\n        assertNull(GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(1);\n        assertEquals(GradeEnum.PRIMARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Integer.class)).thenReturn(2);\n        assertEquals(GradeEnum.SECONDARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n\n        when(resultSet.getObject(\"column\", Character.class)).thenReturn(null);\n        assertNull(CHARACTER_ENUM_MYBATIS_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Character.class)).thenReturn('1');\n        assertEquals(CharacterEnum.MAN, CHARACTER_ENUM_MYBATIS_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n        when(resultSet.getObject(\"column\", Character.class)).thenReturn('2');\n        assertEquals(CharacterEnum.WO_MAN, CHARACTER_ENUM_MYBATIS_ENUM_TYPE_HANDLER.getResult(resultSet, \"column\"));\n    }\n\n    @Test\n    void dealEnumType() {\n        Assertions.assertFalse(MybatisEnumTypeHandler.findEnumValueFieldName(String.class).isPresent());\n        Assertions.assertTrue(MybatisEnumTypeHandler.findEnumValueFieldName(GradeEnum.class).isPresent());\n        Assertions.assertFalse(MybatisEnumTypeHandler.findEnumValueFieldName(SexEnum.class).isPresent());\n        Assertions.assertFalse(MybatisEnumTypeHandler.findEnumValueFieldName(String.class).isPresent());\n        Assertions.assertTrue(MybatisEnumTypeHandler.findEnumValueFieldName(GradeEnum.class).isPresent());\n        Assertions.assertFalse(MybatisEnumTypeHandler.findEnumValueFieldName(SexEnum.class).isPresent());\n    }\n\n    @Test\n    @Override\n    public void setParameter() throws Exception {\n        SEX_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 1, SexEnum.MAN, null);\n        verify(preparedStatement).setObject(1, 1);\n        SEX_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 2, SexEnum.WO_MAN, null);\n        verify(preparedStatement).setObject(2, 2);\n        SEX_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 3, null, JdbcType.INTEGER);\n        verify(preparedStatement).setNull(3, JdbcType.INTEGER.TYPE_CODE);\n\n        GRADE_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 4, GradeEnum.PRIMARY, null);\n        verify(preparedStatement).setObject(4, 1);\n        GRADE_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 5, GradeEnum.SECONDARY, null);\n        verify(preparedStatement).setObject(5, 2);\n        GRADE_ENUM_ENUM_TYPE_HANDLER.setParameter(preparedStatement, 6, null, JdbcType.INTEGER);\n        verify(preparedStatement).setNull(6, JdbcType.INTEGER.TYPE_CODE);\n    }\n\n    @Test\n    @Override\n    public void getResultFromResultSetByColumnIndex() throws Exception {\n        when(resultSet.getObject(1, Integer.class)).thenReturn(1);\n        assertEquals(SexEnum.MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 1));\n        when(resultSet.getObject(2, Integer.class)).thenReturn(2);\n        assertEquals(SexEnum.WO_MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 2));\n        when(resultSet.getObject(3, Integer.class)).thenReturn(null);\n        assertNull(SEX_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 3));\n\n        when(resultSet.getObject(4, Integer.class)).thenReturn(1);\n        assertEquals(GradeEnum.PRIMARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 4));\n        when(resultSet.getObject(5, Integer.class)).thenReturn(2);\n        assertEquals(GradeEnum.SECONDARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 5));\n        when(resultSet.getObject(6, Integer.class)).thenReturn(null);\n        assertNull(GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(resultSet, 6));\n    }\n\n    @Test\n    @Override\n    public void getResultFromCallableStatement() throws Exception {\n        when(callableStatement.getObject(1, Integer.class)).thenReturn(1);\n        assertEquals(SexEnum.MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 1));\n        when(callableStatement.getObject(2, Integer.class)).thenReturn(2);\n        assertEquals(SexEnum.WO_MAN, SEX_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 2));\n        when(callableStatement.getObject(3, Integer.class)).thenReturn(null);\n        assertNull(SEX_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 3));\n\n        when(callableStatement.getObject(4, Integer.class)).thenReturn(1);\n        assertEquals(GradeEnum.PRIMARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 4));\n        when(callableStatement.getObject(5, Integer.class)).thenReturn(2);\n        assertEquals(GradeEnum.SECONDARY, GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 5));\n        when(callableStatement.getObject(6, Integer.class)).thenReturn(null);\n        assertNull(GRADE_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 6));\n    }\n\n    @Test\n    public void testNullButReturnZero() throws Exception {\n        when(callableStatement.getObject(1, Integer.class)).thenReturn(0);\n        when(callableStatement.wasNull()).thenReturn(true);\n        assertNull(SEX_ENUM_ENUM_TYPE_HANDLER.getResult(callableStatement, 1));\n    }\n\n    @Getter\n    @AllArgsConstructor\n    enum SexEnum implements IEnum<Integer> {\n\n        MAN(1, \"1\"),\n        WO_MAN(2, \"2\");\n        final Integer code;\n        final String desc;\n\n        @Override\n        public Integer getValue() {\n            return this.code;\n        }\n    }\n\n    @Getter\n    @AllArgsConstructor\n    enum GradeEnum {\n\n        PRIMARY(1, \"小学\"),\n        SECONDARY(2, \"中学\"),\n        HIGH(3, \"高中\");\n\n        @EnumValue\n        private final int code;\n\n        private final String desc;\n    }\n\n    @Getter\n    @AllArgsConstructor\n    enum CharacterEnum {\n        MAN('1', \"男\"),\n        WO_MAN('2', \"女\");\n\n        @EnumValue\n        final char code;\n        final String desc;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/handlers/MybatisMapWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.test.handlers;\n\nimport com.baomidou.mybatisplus.extension.handlers.MybatisMapWrapper;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2019-03-21\n */\nclass MybatisMapWrapperTest {\n\n    private final MybatisMapWrapper mapWrapper = new MybatisMapWrapper(null, Collections.emptyMap());\n\n    @Test\n    void findProperty() {\n        assertThat(mapWrapper.findProperty(\"xxx\", true)).isEqualTo(\"xxx\");\n        assertThat(mapWrapper.findProperty(\"xxx_sss\", true)).isEqualTo(\"xxxSss\");\n        assertThat(mapWrapper.findProperty(\"xxx_sss_eee\", true)).isEqualTo(\"xxxSssEee\");\n        assertThat(mapWrapper.findProperty(\"XXX_SSS_EEE\", true)).isEqualTo(\"xxxSssEee\");\n        assertThat(mapWrapper.findProperty(\"xxxSss\", true)).isEqualTo(\"xxxSss\");\n        /* 注意一下情况不支持 */\n\n        // 1.不包含下划线,并且首字母大写,会被全部转成小写\n        assertThat(mapWrapper.findProperty(\"SxxSss\", true)).isEqualTo(\"sxxsss\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/model/UserBean.java",
    "content": "package com.baomidou.mybatisplus.test.model;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * @author nieqiurong 2020/2/28.\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class UserBean implements Serializable {\n    \n    private Long id;\n    \n    private String name;\n    \n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/plugins/pagination/DialectFactoryTest.java",
    "content": "package com.baomidou.mybatisplus.test.plugins.pagination;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.GaussDBDialect;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.OracleDialect;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.PostgreDialect;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong\n */\npublic class DialectFactoryTest {\n\n    @Test\n    void test() {\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.MYSQL));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.MARIADB));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.GBASE));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.OSCAR));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.XU_GU));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.CLICK_HOUSE));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.OCEAN_BASE));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.CUBRID));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.SUNDB));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.GOLDENDB));\n        Assertions.assertInstanceOf(MySqlDialect.class, DialectFactory.getDialect(DbType.YASDB));\n\n        Assertions.assertInstanceOf(OracleDialect.class, DialectFactory.getDialect(DbType.DM));\n        Assertions.assertInstanceOf(OracleDialect.class, DialectFactory.getDialect(DbType.ORACLE));\n        Assertions.assertInstanceOf(OracleDialect.class, DialectFactory.getDialect(DbType.GAUSS));\n\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.POSTGRE_SQL));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.H2));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.LEALONE));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.SQLITE));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.HSQL));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.KINGBASE_ES));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.PHOENIX));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.SAP_HANA));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.IMPALA));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.HIGH_GO));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.VERTICA));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.REDSHIFT));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.OPENGAUSS));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.TDENGINE));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.UXDB));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.GBASE8S_PG));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.GBASE_8C));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.VASTBASE));\n        Assertions.assertInstanceOf(PostgreDialect.class, DialectFactory.getDialect(DbType.DUCKDB));\n\n        Assertions.assertInstanceOf(GaussDBDialect.class, DialectFactory.getDialect(DbType.GAUSS_DB));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/plugins/pagination/SQLServer2005DialectTest.java",
    "content": "package com.baomidou.mybatisplus.test.plugins.pagination;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.SQLServer2005Dialect;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong\n */\npublic class SQLServer2005DialectTest {\n\n    private final SQLServer2005Dialect sqlServer2005Dialect = new SQLServer2005Dialect();\n\n    @Test\n    void test() {\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"select distinct *,(select 1) from test\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" select distinct *,(select 1) from test\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"select *,(select 1) from test\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" select *,(select 1) from test\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"select   distinct   *,(select 1) from test\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" select   distinct *,(select 1) from test\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"select   *,(select 1) from test\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(select 1) from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" select   *,(select 1) from test\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  * from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"select * from test\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  * from test) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" select * from test\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(SELECT 1) FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"SELECT DISTINCT *,(SELECT 1) FROM TEST\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT DISTINCT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(SELECT 1) FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" SELECT DISTINCT *,(SELECT 1) FROM TEST\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(SELECT 1) FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"SELECT *,(SELECT 1) FROM TEST\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  *,(SELECT 1) FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" SELECT *,(SELECT 1) FROM TEST\", 1, 10).getDialectSql());\n\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  * FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\"SELECT * FROM TEST\", 1, 10).getDialectSql());\n        Assertions.assertEquals(\"WITH selectTemp AS (SELECT TOP 100 PERCENT  ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __row_number__,  * FROM TEST) SELECT * FROM selectTemp WHERE __row_number__ BETWEEN 2 AND 11 ORDER BY __row_number__\",\n            sqlServer2005Dialect.buildPaginationSql(\" SELECT * FROM TEST\", 1, 10).getDialectSql());\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\")) {\n            JerryParser jerryParser = Jerry.create(new LagartoDOMBuilder().enableXmlMode());\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency core = dependenciesMap.get(\"mybatis-plus-core\");\n            Assertions.assertEquals(\"compile\", core.getScope());\n            Assertions.assertFalse(core.isOptional());\n            Dependency kotlinStdlib = dependenciesMap.get(\"kotlin-stdlib-jdk8\");\n            Assertions.assertEquals(\"compile\", kotlinStdlib.getScope());\n            Assertions.assertTrue(kotlinStdlib.isOptional());\n            Dependency kotlinReflect = dependenciesMap.get(\"kotlin-reflect\");\n            Assertions.assertEquals(\"compile\", kotlinReflect.getScope());\n            Assertions.assertTrue(kotlinReflect.isOptional());\n            Dependency slf4jApi = dependenciesMap.get(\"slf4j-api\");\n            Assertions.assertEquals(\"compile\", slf4jApi.getScope());\n            Assertions.assertTrue(slf4jApi.isOptional());\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/resources/ddl/test.sql",
    "content": "-- run sql test;\n"
  },
  {
    "path": "mybatis-plus-extension/src/test/resources/logback.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration scan=\"false\" debug=\"false\">\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoder 默认配置为PatternLayoutEncoder -->\n        <encoder>\n            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <logger name=\"com.baomidou.mybatisplus.test\" level=\"DEBUG\" additivity=\"false\">\n        <appender-ref ref=\"STDOUT\"/>\n    </logger>\n\n    <root level=\"info\">\n        <appender-ref ref=\"STDOUT\"/>\n    </root>\n</configuration>\n"
  },
  {
    "path": "mybatis-plus-generator/build.gradle",
    "content": "apply plugin: 'kotlin'\n\ncompileTestKotlin {\n    kotlinOptions {\n        freeCompilerArgs = ['-Xjvm-default=all']\n    }\n}\ndependencies {\n    implementation project(\":mybatis-plus-spring\")\n    implementation \"${lib.velocity}\"\n    implementation \"${lib.freemarker}\"\n    implementation \"${lib.beetl}\"\n    implementation \"${lib.enjoy}\"\n    implementation \"${lib.'swagger-annotations'}\"\n    implementation \"io.springfox:springfox-swagger2:3.0.0\"\n    implementation \"${lib.\"kotlin-stdlib-jdk8\"}\"\n\n\n    compileOnly \"org.jetbrains:annotations:24.1.0\"\n    testImplementation \"${lib.sqlserver}\"\n    testImplementation \"${lib.postgresql}\"\n    testImplementation \"${lib.oracle}\"\n    testImplementation lib.dm as ConfigurableFileTree\n    testImplementation \"${lib.h2}\"\n    testImplementation \"${lib.mysql}\"\n    testImplementation \"${lib.sqlite}\"\n    testImplementation \"${lib.firebird}\"\n    testImplementation \"${lib.gaussdb}\"\n    testImplementation \"${lib.'swagger-annotations'}\"\n    testImplementation \"${lib.'logback-classic'}\"\n    testImplementation \"${lib.'spring-web'}\"\n    testCompileOnly \"org.jetbrains:annotations:24.1.0\"\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/AutoGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.generator.config.*;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;\nimport com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\n/**\n * 生成文件\n *\n * @author YangHu, tangguo, hubin\n * @since 2016-08-30\n */\n@Getter\npublic class AutoGenerator {\n\n    private static final Logger logger = LoggerFactory.getLogger(AutoGenerator.class);\n\n    /**\n     * 配置信息\n     */\n    protected ConfigBuilder config;\n    /**\n     * 注入配置\n     */\n    protected InjectionConfig injection;\n    /**\n     * 数据源配置\n     */\n    private DataSourceConfig dataSource;\n    /**\n     * 数据库表配置\n     */\n    private StrategyConfig strategy;\n    /**\n     * 包 相关配置\n     */\n    private PackageConfig packageInfo;\n    /**\n     * 模板 相关配置\n     * @deprecated 3.5.6 {@link #strategy}\n     */\n    @Deprecated\n    private TemplateConfig template;\n    /**\n     * 全局 相关配置\n     */\n    private GlobalConfig globalConfig;\n\n    private AutoGenerator() {\n        // 不推荐使用\n    }\n\n    /**\n     * 构造方法\n     *\n     * @param dataSourceConfig 数据库配置\n     * @since 3.5.0\n     */\n    public AutoGenerator(@NotNull DataSourceConfig dataSourceConfig) {\n        //这个是必须参数,其他都是可选的,后续去除默认构造更改成final\n        this.dataSource = dataSourceConfig;\n    }\n\n    /**\n     * 注入配置\n     *\n     * @param injectionConfig 注入配置\n     * @return this\n     * @since 3.5.0\n     */\n    public AutoGenerator injection(@NotNull InjectionConfig injectionConfig) {\n        this.injection = injectionConfig;\n        return this;\n    }\n\n    /**\n     * 生成策略\n     *\n     * @param strategyConfig 策略配置\n     * @return this\n     * @since 3.5.0\n     */\n    public AutoGenerator strategy(@NotNull StrategyConfig strategyConfig) {\n        this.strategy = strategyConfig;\n        return this;\n    }\n\n    /**\n     * 指定包配置信息\n     *\n     * @param packageConfig 包配置\n     * @return this\n     * @since 3.5.0\n     */\n    public AutoGenerator packageInfo(@NotNull PackageConfig packageConfig) {\n        this.packageInfo = packageConfig;\n        return this;\n    }\n\n    /**\n     * 指定模板配置\n     *\n     * @param templateConfig 模板配置\n     * @return this\n     * @deprecated 3.5.6 {@link #strategy(StrategyConfig)}\n     * @since 3.5.0\n     */\n    @Deprecated\n    public AutoGenerator template(@NotNull TemplateConfig templateConfig) {\n        this.template = templateConfig;\n        return this;\n    }\n\n    /**\n     * 指定全局配置\n     *\n     * @param globalConfig 全局配置\n     * @return this\n     * @since  3.5.0\n     */\n    public AutoGenerator global(@NotNull GlobalConfig globalConfig) {\n        this.globalConfig = globalConfig;\n        return this;\n    }\n\n    /**\n     * 设置配置汇总\n     *\n     * @param configBuilder 配置汇总\n     * @return this\n     * @since 3.5.0\n     */\n    public AutoGenerator config(@NotNull ConfigBuilder configBuilder) {\n        this.config = configBuilder;\n        return this;\n    }\n\n    /**\n     * 生成代码\n     */\n    public void execute() {\n        this.execute(null);\n    }\n\n    /**\n     * 生成代码\n     *\n     * @param templateEngine 模板引擎\n     */\n    public void execute(AbstractTemplateEngine templateEngine) {\n        logger.debug(\"==========================Ready to generate the file...==========================\");\n        // 初始化配置\n        if (null == config) {\n            config = new ConfigBuilder(packageInfo, dataSource, strategy, template, globalConfig, injection);\n        }\n        if (null == templateEngine) {\n            // 为了兼容之前逻辑，采用 Velocity 引擎 【 默认 】\n            templateEngine = new VelocityTemplateEngine();\n        }\n        templateEngine.setConfigBuilder(config);\n        // 模板引擎初始化执行文件输出\n        templateEngine.init(config).batchOutput().open();\n        logger.debug(\"==========================The file is generated！！！==========================\");\n    }\n\n    /**\n     * 开放表信息、预留子类重写\n     *\n     * @param config 配置信息\n     * @return ignore\n     */\n    @NotNull\n    protected List<TableInfo> getAllTableInfoList(@NotNull ConfigBuilder config) {\n        return config.getTableInfoList();\n    }\n\n    public InjectionConfig getInjectionConfig() {\n        return injection;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/DefaultTableAnnotationHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.model.ClassAnnotationAttributes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 表注解处理器\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic class DefaultTableAnnotationHandler implements ITableAnnotationHandler {\n\n    @Override\n    public List<ClassAnnotationAttributes> handle(TableInfo tableInfo, Entity entity) {\n        List<ClassAnnotationAttributes> annotationAttributesList = new ArrayList<>();\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        String comment = tableInfo.getComment();\n        if (StringUtils.isBlank(comment)) {\n            comment = StringPool.EMPTY;\n        }\n        boolean kotlin = globalConfig.isKotlin();\n        if (!kotlin) {\n            // 原先kt模板没有处理这些,作为兼容项\n            if (entity.isChain() && entity.isLombok()) {\n                annotationAttributesList.add(new ClassAnnotationAttributes(\"@Accessors(chain = true)\", \"lombok.experimental.Accessors\"));\n            }\n            if (entity.isLombok()) {\n                if (entity.isDefaultLombok()) {\n                    // 原先lombok默认只有这两个\n                    annotationAttributesList.add(new ClassAnnotationAttributes(\"@Getter\", \"lombok.Getter\"));\n                    annotationAttributesList.add(new ClassAnnotationAttributes(\"@Setter\", \"lombok.Setter\"));\n                    if (entity.isToString()) {\n                        annotationAttributesList.add(new ClassAnnotationAttributes(\"@ToString\", \"lombok.ToString\"));\n                    }\n                }\n            }\n        }\n        if (tableInfo.isConvert()) {\n            String schemaName = tableInfo.getSchemaName();\n            if (StringUtils.isBlank(schemaName)) {\n                schemaName = StringPool.EMPTY;\n            } else {\n                schemaName = schemaName + StringPool.DOT;\n            }\n            //@TableName(\"${schemaName}${table.name}\")\n            String displayName = String.format(\"@TableName(\\\"%s%s\\\")\", schemaName, tableInfo.getName());\n            annotationAttributesList.add(new ClassAnnotationAttributes(TableName.class, displayName));\n        }\n        if (globalConfig.isSwagger()) {\n            //@ApiModel(value = \"${entity}对象\", description = \"${table.comment!}\")\n            String displayName = String.format(\"@ApiModel(value = \\\"%s对象\\\", description = \\\"%s\\\")\", tableInfo.getEntityName(), comment);\n            annotationAttributesList.add(new ClassAnnotationAttributes(\n                displayName, \"io.swagger.annotations.ApiModel\", \"io.swagger.annotations.ApiModelProperty\"));\n        }\n        if (globalConfig.isSpringdoc()) {\n            //@Schema(name = \"${entity}\", description = \"${table.comment!}\")\n            String displayName = String.format(\"@Schema(name = \\\"%s\\\", description = \\\"%s\\\")\", tableInfo.getEntityName(), comment);\n            annotationAttributesList.add(new ClassAnnotationAttributes(displayName, \"io.swagger.v3.oas.annotations.media.Schema\"));\n        }\n        return annotationAttributesList;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/DefaultTableFieldAnnotationHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.Version;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.model.AnnotationAttributes;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 默认字段注解处理器\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic class DefaultTableFieldAnnotationHandler implements ITableFieldAnnotationHandler {\n\n    @Override\n    public List<AnnotationAttributes> handle(TableInfo tableInfo, TableField tableField) {\n        List<AnnotationAttributes> annotationAttributesList = new ArrayList<>();\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        Entity entity = tableField.getEntity();\n        String comment = tableField.getComment();\n        if (StringUtils.isNotBlank(comment)) {\n            if (globalConfig.isSpringdoc()) {\n                //@Schema(description = \"${field.comment}\")\n                String displayName = String.format(\"@Schema(description = \\\"%s\\\")\", comment);\n                annotationAttributesList.add(new AnnotationAttributes(displayName, \"io.swagger.v3.oas.annotations.media.Schema\"));\n            } else if (globalConfig.isSwagger()) {\n                String displayName = String.format(\"@ApiModelProperty(\\\"%s\\\")\", comment);\n                annotationAttributesList.add(new AnnotationAttributes(displayName, \"io.swagger.annotations.ApiModelProperty\"));\n            }\n        }\n        if (tableField.isKeyFlag()) {\n            IdType idType = entity.getIdType();\n            if (tableField.isKeyIdentityFlag()) {\n                //@TableId(value = \"${field.annotationColumnName}\", type = IdType.AUTO)\n                String displayName = String.format(\"@TableId(value = \\\"%s\\\", type = IdType.AUTO)\", tableField.getAnnotationColumnName());\n                annotationAttributesList.add(new AnnotationAttributes(displayName, TableId.class.getName()));\n            } else if (idType != null) {\n                //@TableId(value = \"${field.annotationColumnName}\", type = IdType.${idType})\n                String displayName = String.format(\"@TableId(value = \\\"%s\\\", type = IdType.%s)\", tableField.getAnnotationColumnName(), idType);\n                annotationAttributesList.add(new AnnotationAttributes(displayName, TableId.class.getName()));\n            } else if (tableField.isConvert()) {\n                //@TableId(\"${field.annotationColumnName}\")\n                String displayName = String.format(\"@TableId(\\\"%s\\\")\", tableField.getAnnotationColumnName());\n                annotationAttributesList.add(new AnnotationAttributes(displayName, TableId.class.getName()));\n            }\n        } else {\n            String fill = tableField.getFill();\n            if (StringUtils.isNotBlank(fill)) {\n                if (tableField.isConvert()) {\n                    //@TableField(value = \"${field.annotationColumnName}\", fill = FieldFill.${field.fill})\n                    String displayName = String.format(\"@TableField(value = \\\"%s\\\", fill = FieldFill.%s)\", tableField.getAnnotationColumnName(), fill);\n                    annotationAttributesList.add(new AnnotationAttributes(displayName, com.baomidou.mybatisplus.annotation.TableField.class.getName()));\n                } else {\n                    //@TableField(fill = FieldFill.${field.fill})\n                    String displayName = String.format(\"@TableField(fill = FieldFill.%s)\", fill);\n                    annotationAttributesList.add(new AnnotationAttributes(displayName, com.baomidou.mybatisplus.annotation.TableField.class.getName()));\n                }\n            } else {\n                if (tableField.isConvert()) {\n                    //@TableField(\"${field.annotationColumnName}\")\n                    String displayName = String.format(\"@TableField(\\\"%s\\\")\", tableField.getAnnotationColumnName());\n                    annotationAttributesList.add(new AnnotationAttributes(displayName, com.baomidou.mybatisplus.annotation.TableField.class.getName()));\n                }\n            }\n            if (tableField.isVersionField()) {\n                // @Version\n                annotationAttributesList.add(new AnnotationAttributes(Version.class));\n            }\n            if (tableField.isLogicDeleteField()) {\n                //@TableLogic\n                annotationAttributesList.add(new AnnotationAttributes(TableLogic.class));\n            }\n        }\n        return annotationAttributesList;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/FastAutoGenerator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.*;\nimport com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Scanner;\nimport java.util.function.BiConsumer;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\n/**\n * mybatis plus FastAutoGenerator\n *\n * @author L.cm, lanjerry\n * @since 2021-07-22\n */\npublic final class FastAutoGenerator {\n\n    /**\n     * 数据源配置 Builder\n     */\n    private final DataSourceConfig.Builder dataSourceConfigBuilder;\n\n    /**\n     * 全局配置 Builder\n     */\n    private final GlobalConfig.Builder globalConfigBuilder;\n\n    /**\n     * 包配置 Builder\n     */\n    private final PackageConfig.Builder packageConfigBuilder;\n\n    /**\n     * 策略配置 Builder\n     */\n    private final StrategyConfig.Builder strategyConfigBuilder;\n\n    /**\n     * 注入配置 Builder\n     */\n    private final InjectionConfig.Builder injectionConfigBuilder;\n\n    /**\n     * 模板配置 Builder\n     * @deprecated 3.5.6 {@link #strategyConfigBuilder}\n     */\n    @Deprecated\n    private final TemplateConfig.Builder templateConfigBuilder;\n\n    /**\n     * 模板引擎\n     */\n    private AbstractTemplateEngine templateEngine;\n\n    private FastAutoGenerator(DataSourceConfig.Builder dataSourceConfigBuilder) {\n        this.dataSourceConfigBuilder = dataSourceConfigBuilder;\n        this.globalConfigBuilder = new GlobalConfig.Builder();\n        this.packageConfigBuilder = new PackageConfig.Builder();\n        this.strategyConfigBuilder = new StrategyConfig.Builder();\n        this.injectionConfigBuilder = new InjectionConfig.Builder();\n        this.templateConfigBuilder = new TemplateConfig.Builder();\n    }\n\n    public static FastAutoGenerator create(@NotNull String url, String username, String password) {\n        return new FastAutoGenerator(new DataSourceConfig.Builder(url, username, password));\n    }\n\n    public static FastAutoGenerator create(@NotNull DataSourceConfig.Builder dataSourceConfigBuilder) {\n        return new FastAutoGenerator(dataSourceConfigBuilder);\n    }\n\n    /**\n     * 读取控制台输入内容\n     */\n    private final Scanner scanner = new Scanner(System.in);\n\n    /**\n     * 控制台输入内容读取并打印提示信息\n     *\n     * @param message 提示信息\n     * @return String\n     */\n    public String scannerNext(String message) {\n        System.out.println(message);\n        String nextLine = scanner.nextLine();\n        if (StringUtils.isBlank(nextLine)) {\n            // 如果输入空行继续等待\n            return scanner.next();\n        }\n        return nextLine;\n    }\n\n    /**\n     * 全局配置\n     *\n     * @param consumer 自定义全局配置\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator dataSourceConfig(Consumer<DataSourceConfig.Builder> consumer) {\n        consumer.accept(this.dataSourceConfigBuilder);\n        return this;\n    }\n\n    public FastAutoGenerator dataSourceConfig(BiConsumer<Function<String, String>, DataSourceConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.dataSourceConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 全局配置\n     *\n     * @param consumer 自定义全局配置\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator globalConfig(Consumer<GlobalConfig.Builder> consumer) {\n        consumer.accept(this.globalConfigBuilder);\n        return this;\n    }\n\n    public FastAutoGenerator globalConfig(BiConsumer<Function<String, String>, GlobalConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.globalConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 包配置\n     *\n     * @param consumer 自定义包配置\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator packageConfig(Consumer<PackageConfig.Builder> consumer) {\n        consumer.accept(this.packageConfigBuilder);\n        return this;\n    }\n\n    public FastAutoGenerator packageConfig(BiConsumer<Function<String, String>, PackageConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.packageConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 策略配置\n     *\n     * @param consumer 自定义策略配置\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator strategyConfig(Consumer<StrategyConfig.Builder> consumer) {\n        consumer.accept(this.strategyConfigBuilder);\n        return this;\n    }\n\n    public FastAutoGenerator strategyConfig(BiConsumer<Function<String, String>, StrategyConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.strategyConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 注入配置\n     *\n     * @param consumer 自定义注入配置\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator injectionConfig(Consumer<InjectionConfig.Builder> consumer) {\n        consumer.accept(this.injectionConfigBuilder);\n        return this;\n    }\n\n    public FastAutoGenerator injectionConfig(BiConsumer<Function<String, String>, InjectionConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.injectionConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 模板配置\n     *\n     * @param consumer 自定义模板配置\n     * @return FastAutoGenerator\n     * @deprecated 3.5.6 {@link #strategyConfig(Consumer)}\n     */\n    @Deprecated\n    public FastAutoGenerator templateConfig(Consumer<TemplateConfig.Builder> consumer) {\n        consumer.accept(this.templateConfigBuilder);\n        return this;\n    }\n\n    /**\n     * @param biConsumer\n     * @return FastAutoGenerator\n     * @deprecated 3.5.6 {@link #strategyConfig(BiConsumer)}\n     */\n    @Deprecated\n    public FastAutoGenerator templateConfig(BiConsumer<Function<String, String>, TemplateConfig.Builder> biConsumer) {\n        biConsumer.accept(this::scannerNext, this.templateConfigBuilder);\n        return this;\n    }\n\n    /**\n     * 模板引擎配置\n     *\n     * @param templateEngine 模板引擎\n     * @return FastAutoGenerator\n     */\n    public FastAutoGenerator templateEngine(AbstractTemplateEngine templateEngine) {\n        this.templateEngine = templateEngine;\n        return this;\n    }\n\n    public void execute() {\n        new AutoGenerator(this.dataSourceConfigBuilder.build())\n            // 全局配置\n            .global(this.globalConfigBuilder.build())\n            // 包配置\n            .packageInfo(this.packageConfigBuilder.build())\n            // 策略配置\n            .strategy(this.strategyConfigBuilder.build())\n            // 注入配置\n            .injection(this.injectionConfigBuilder.build())\n            // 模板配置\n            .template(this.templateConfigBuilder.build())\n            // 执行\n            .execute(this.templateEngine);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/IFill.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 填充接口\n *\n * @author nieqiurong\n * @since 3.5.0 2020/11/30.\n */\npublic interface IFill {\n\n    @NotNull\n    String getName();\n\n    @NotNull\n    FieldFill getFieldFill();\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/IGenerateMapperMethodHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.model.MapperMethod;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Mapper层方法生成处理器\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic interface IGenerateMapperMethodHandler {\n\n    /**\n     * 获取生成方法\n     *\n     * @param tableInfo 表信息\n     * @return 索引方法\n     */\n    List<MapperMethod> getMethodList(TableInfo tableInfo);\n\n    /**\n     * 获取需要导入的包\n     *\n     * @param tableInfo 表信息\n     * @return 导包列表\n     */\n    Set<String> getImportPackages(TableInfo tableInfo);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/ITableAnnotationHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.model.ClassAnnotationAttributes;\n\nimport java.util.List;\n\n/**\n * 表注解处理器\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic interface ITableAnnotationHandler {\n\n    /**\n     * 处理字段级注解\n     *\n     * @param tableInfo 表信息\n     * @return 注解信息\n     */\n    List<ClassAnnotationAttributes> handle(TableInfo tableInfo, Entity entity);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/ITableFieldAnnotationHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.model.AnnotationAttributes;\n\nimport java.util.List;\n\n/**\n * 字段注解处理器\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic interface ITableFieldAnnotationHandler {\n\n    /**\n     * 处理字段级注解\n     *\n     * @param tableInfo  表信息\n     * @param tableField 字段信息\n     * @return 注解信息\n     */\n    List<AnnotationAttributes> handle(TableInfo tableInfo, TableField tableField);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/ITemplate.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\n/**\n * 渲染模板接口\n *\n * @author nieqiurong 2020/11/9.\n * @since 3.5.0\n */\npublic interface ITemplate extends Serializable {\n\n    @NotNull\n    Map<String, Object> renderData(@NotNull TableInfo tableInfo);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/ConstVal.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\n\nimport java.nio.charset.StandardCharsets;\n\n/**\n * 定义常量\n *\n * @author YangHu, tangguo, hubin\n * @since 2016-08-31\n */\npublic interface ConstVal {\n\n    String MODULE_NAME = \"ModuleName\";\n\n    String ENTITY = \"Entity\";\n    String SERVICE = \"Service\";\n    String SERVICE_IMPL = \"ServiceImpl\";\n    String MAPPER = \"Mapper\";\n    String XML = \"Xml\";\n    String CONTROLLER = \"Controller\";\n    String PARENT = \"Parent\";\n\n    String JAVA_TMPDIR = \"java.io.tmpdir\";\n    String UTF8 = StandardCharsets.UTF_8.name();\n    String UNDERLINE = \"_\";\n\n    String JAVA_SUFFIX = StringPool.DOT_JAVA;\n    String KT_SUFFIX = \".kt\";\n    String XML_SUFFIX = \".xml\";\n\n    /**\n     * 实体模板路径\n     */\n    String TEMPLATE_ENTITY_JAVA = \"/templates/entity.java\";\n\n    /**\n     * 实体模板路径(kotlin模板)\n     */\n    String TEMPLATE_ENTITY_KT = \"/templates/entity.kt\";\n\n    /**\n     * 控制器模板路径\n     */\n    String TEMPLATE_CONTROLLER = \"/templates/controller.java\";\n\n    /**\n     * Mapper模板路径\n     */\n    String TEMPLATE_MAPPER = \"/templates/mapper.java\";\n\n    /**\n     * MapperXml模板路径\n     */\n    String TEMPLATE_XML = \"/templates/mapper.xml\";\n\n    /**\n     * Service模板路径\n     */\n    String TEMPLATE_SERVICE = \"/templates/service.java\";\n\n    /**\n     * ServiceImpl模板路径\n     */\n    String TEMPLATE_SERVICE_IMPL = \"/templates/serviceImpl.java\";\n\n    String VM_LOAD_PATH_KEY = \"file.resource.loader.class\";\n    String VM_LOAD_PATH_VALUE = \"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader\";\n\n    String SUPER_MAPPER_CLASS = \"com.baomidou.mybatisplus.core.mapper.BaseMapper\";\n    String SUPER_SERVICE_CLASS = \"com.baomidou.mybatisplus.extension.service.IService\";\n    String SUPER_SERVICE_IMPL_CLASS = \"com.baomidou.mybatisplus.extension.service.impl.ServiceImpl\";\n\n    /**\n     * @see com.baomidou.mybatisplus.core.metadata.TableInfoHelper.DEFAULT_ID_NAME\n     */\n    String DEFAULT_ID_NAME = \"id\";\n\n    /**\n     * 主键\n     * @since 3.5.12\n     */\n    String PRIMARY = \"PRIMARY\";\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/DataSourceConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;\nimport com.baomidou.mybatisplus.generator.config.converts.TypeConverts;\nimport com.baomidou.mybatisplus.generator.config.querys.DbQueryDecorator;\nimport com.baomidou.mybatisplus.generator.config.querys.DbQueryRegistry;\nimport com.baomidou.mybatisplus.generator.query.AbstractDatabaseQuery;\nimport com.baomidou.mybatisplus.generator.query.DefaultQuery;\nimport com.baomidou.mybatisplus.generator.query.IDatabaseQuery;\nimport com.baomidou.mybatisplus.generator.query.SQLQuery;\nimport com.baomidou.mybatisplus.generator.type.ITypeConvertHandler;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\n\n/**\n * 数据库配置\n *\n * @author YangHu, hcl, hubin\n * @since 2016/8/30\n */\npublic class DataSourceConfig {\n\n    protected final Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);\n\n    private DataSourceConfig() {\n    }\n\n    /**\n     * 数据库信息查询\n     */\n    private IDbQuery dbQuery;\n\n    /**\n     * schemaName\n     */\n    private String schemaName;\n\n    /**\n     * 类型转换\n     */\n    private ITypeConvert typeConvert;\n\n    /**\n     * 关键字处理器\n     *\n     * @since 3.3.2\n     */\n    private IKeyWordsHandler keyWordsHandler;\n\n    /**\n     * 驱动连接的URL\n     */\n    private String url;\n\n    /**\n     * 数据库连接用户名\n     */\n    private String username;\n\n    /**\n     * 数据库连接密码\n     */\n    private String password;\n\n    /**\n     * 数据源实例\n     *\n     * @since 3.5.0\n     */\n    private DataSource dataSource;\n\n    /**\n     * 数据库连接\n     *\n     * @since 3.5.0\n     */\n    private Connection connection;\n\n    /**\n     * 数据库连接属性\n     *\n     * @since 3.5.3\n     */\n    private final Map<String, String> connectionProperties = new HashMap<>();\n\n    /**\n     * 查询方式\n     *\n     * @see DefaultQuery 默认查询方式，配合{@link #getTypeConvertHandler()} 使用\n     * @see SQLQuery SQL语句查询方式，配合{@link #typeConvert} 使用\n     * @since 3.5.3\n     */\n    private Class<? extends AbstractDatabaseQuery> databaseQueryClass = DefaultQuery.class;\n\n    /**\n     * 类型转换处理\n     *\n     * @since 3.5.3\n     */\n    private ITypeConvertHandler typeConvertHandler;\n\n    /**\n     * 驱动全类名\n     *\n     * @since 3.5.8\n     */\n    private String driverClassName;\n\n    /**\n     * 获取数据库查询\n     */\n    @NotNull\n    public IDbQuery getDbQuery() {\n        if (null == dbQuery) {\n            DbType dbType = getDbType();\n            DbQueryRegistry dbQueryRegistry = new DbQueryRegistry();\n            // 默认 MYSQL\n            dbQuery = Optional.ofNullable(dbQueryRegistry.getDbQuery(dbType)).orElseGet(() -> dbQueryRegistry.getDbQuery(DbType.MYSQL));\n        }\n        return dbQuery;\n    }\n\n    /**\n     * 判断数据库类型\n     *\n     * @return 类型枚举值\n     */\n    @NotNull\n    public DbType getDbType() {\n        return this.getDbType(this.url.toLowerCase());\n    }\n\n    /**\n     * 判断数据库类型\n     *\n     * @param str url\n     * @return 类型枚举值，如果没找到，则返回 null\n     */\n    @NotNull\n    private DbType getDbType(@NotNull String str) {\n        if (str.contains(\":mysql:\") || str.contains(\":cobar:\")) {\n            return DbType.MYSQL;\n        } else if (str.contains(\":oracle:\")) {\n            return DbType.ORACLE;\n        } else if (str.contains(\":postgresql:\")) {\n            return DbType.POSTGRE_SQL;\n        } else if (str.contains(\":sqlserver:\")) {\n            return DbType.SQL_SERVER;\n        } else if (str.contains(\":db2:\")) {\n            return DbType.DB2;\n        } else if (str.contains(\":mariadb:\")) {\n            return DbType.MARIADB;\n        } else if (str.contains(\":sqlite:\")) {\n            return DbType.SQLITE;\n        } else if (str.contains(\":h2:\")) {\n            return DbType.H2;\n        } else if (str.contains(\":lealone:\")) {\n            return DbType.LEALONE;\n        } else if (str.contains(\":kingbase:\") || str.contains(\":kingbase8:\")) {\n            return DbType.KINGBASE_ES;\n        } else if (str.contains(\":dm:\")) {\n            return DbType.DM;\n        } else if (str.contains(\":zenith:\")) {\n            return DbType.GAUSS;\n        } else if (str.contains(\":gaussdb:\")) {\n            return DbType.GAUSS_DB;\n        } else if (str.contains(\":oscar:\")) {\n            return DbType.OSCAR;\n        } else if (str.contains(\":firebird:\")) {\n            return DbType.FIREBIRD;\n        } else if (str.contains(\":xugu:\")) {\n            return DbType.XU_GU;\n        } else if (str.contains(\":clickhouse:\")) {\n            return DbType.CLICK_HOUSE;\n        } else if (str.contains(\":sybase:\")) {\n            return DbType.SYBASE;\n        } else {\n            return DbType.OTHER;\n        }\n    }\n\n    /**\n     * 获取数据库字段类型转换\n     */\n    @NotNull\n    public ITypeConvert getTypeConvert() {\n        if (null == typeConvert) {\n            DbType dbType = getDbType();\n            // 默认 MYSQL\n            typeConvert = TypeConverts.getTypeConvert(dbType);\n            if (null == typeConvert) {\n                typeConvert = MySqlTypeConvert.INSTANCE;\n            }\n        }\n        return typeConvert;\n    }\n\n    /**\n     * 创建数据库连接对象\n     * 这方法建议只调用一次，毕竟只是代码生成，用一个连接就行。\n     *\n     * @return Connection\n     * @see DbQueryDecorator#getConnection()\n     */\n    @NotNull\n    public Connection getConn() {\n        try {\n            if (connection != null && !connection.isClosed()) {\n                return connection;\n            } else {\n                synchronized (this) {\n                    if (dataSource != null) {\n                        connection = dataSource.getConnection();\n                    } else {\n                        Properties properties = new Properties();\n                        connectionProperties.forEach(properties::setProperty);\n                        properties.put(\"user\", username);\n                        properties.put(\"password\", password);\n                        // 使用元数据查询方式时，有些数据库需要增加属性才能读取注释\n                        this.processProperties(properties);\n                        this.connection = DriverManager.getConnection(url, properties);\n                        if (StringUtils.isBlank(this.schemaName)) {\n                            try {\n                                this.schemaName = connection.getSchema();\n                            } catch (Exception exception) {\n                                // ignore 老古董1.7以下的驱动不支持.\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        return connection;\n    }\n\n    private void processProperties(Properties properties) {\n        if (this.databaseQueryClass.getName().equals(DefaultQuery.class.getName())) {\n            switch (this.getDbType()) {\n                case MYSQL:\n                    properties.put(\"remarks\", \"true\");\n                    properties.put(\"useInformationSchema\", \"true\");\n                    break;\n                case ORACLE:\n                    properties.put(\"remarks\", \"true\");\n                    properties.put(\"remarksReporting\", \"true\");\n                    break;\n            }\n        }\n    }\n\n    /**\n     * 获取数据库默认schema\n     *\n     * @return 默认schema\n     * @since 3.5.0\n     */\n    @Nullable\n    protected String getDefaultSchema() {\n        DbType dbType = getDbType();\n        String schema = null;\n        if (DbType.POSTGRE_SQL == dbType) {\n            //pg 默认 schema=public\n            schema = \"public\";\n        } else if (DbType.KINGBASE_ES == dbType) {\n            //kingbase 默认 schema=PUBLIC\n            schema = \"PUBLIC\";\n        } else if (DbType.DB2 == dbType) {\n            //db2 默认 schema=current schema\n            schema = \"current schema\";\n        } else if (DbType.ORACLE == dbType) {\n            //oracle 默认 schema=username\n            schema = this.username.toUpperCase();\n        }\n        return schema;\n    }\n\n    @Nullable\n    public String getSchemaName() {\n        return schemaName;\n    }\n\n    @Nullable\n    public IKeyWordsHandler getKeyWordsHandler() {\n        return keyWordsHandler;\n    }\n\n    @NotNull\n    public String getUrl() {\n        return url;\n    }\n\n    @Nullable\n    public String getUsername() {\n        return username;\n    }\n\n    @Nullable\n    public String getPassword() {\n        return password;\n    }\n\n    @NotNull\n    public Class<? extends IDatabaseQuery> getDatabaseQueryClass() {\n        return databaseQueryClass;\n    }\n\n    @Nullable\n    public ITypeConvertHandler getTypeConvertHandler() {\n        return typeConvertHandler;\n    }\n\n    @Nullable\n    public String getDriverClassName() {\n        return driverClassName;\n    }\n\n    /**\n     * 数据库配置构建者\n     *\n     * @author nieqiurong 2020/10/10.\n     * @since 3.5.0\n     */\n    public static class Builder implements IConfigBuilder<DataSourceConfig> {\n\n        private final DataSourceConfig dataSourceConfig;\n\n        private Builder() {\n            this.dataSourceConfig = new DataSourceConfig();\n        }\n\n        /**\n         * 构造初始化方法\n         *\n         * @param url      数据库连接地址\n         * @param username 数据库账号\n         * @param password 数据库密码\n         */\n        public Builder(@NotNull String url, String username, String password) {\n            this();\n            if (StringUtils.isBlank(url)) {\n                throw new IllegalArgumentException(\"`url` cannot be empty\");\n            }\n            this.dataSourceConfig.url = url;\n            this.dataSourceConfig.username = username;\n            this.dataSourceConfig.password = password;\n        }\n\n        /**\n         * 构造初始化方法\n         *\n         * @param dataSource 外部数据源实例\n         */\n        public Builder(@NotNull DataSource dataSource) {\n            this();\n            this.dataSourceConfig.dataSource = dataSource;\n            try {\n                Connection conn = dataSource.getConnection();\n                this.dataSourceConfig.url = conn.getMetaData().getURL();\n                try {\n                    this.dataSourceConfig.schemaName = conn.getSchema();\n                } catch (Throwable exception) {\n                    //ignore  如果使用低版本的驱动，这里由于是1.7新增的方法，所以会报错，如果驱动太低，需要自行指定了。\n                }\n                this.dataSourceConfig.connection = conn;\n                this.dataSourceConfig.username = conn.getMetaData().getUserName();\n            } catch (SQLException ex) {\n                throw new RuntimeException(ex);\n            }\n        }\n\n        /**\n         * 设置数据库查询实现\n         *\n         * @param dbQuery 数据库查询实现\n         * @return this\n         */\n        public Builder dbQuery(@NotNull IDbQuery dbQuery) {\n            this.dataSourceConfig.dbQuery = dbQuery;\n            return this;\n        }\n\n        /**\n         * 设置数据库schema\n         *\n         * @param schemaName 数据库schema\n         * @return this\n         */\n        public Builder schema(@NotNull String schemaName) {\n            this.dataSourceConfig.schemaName = schemaName;\n            return this;\n        }\n\n        /**\n         * 设置类型转换器\n         *\n         * @param typeConvert 类型转换器\n         * @return this\n         */\n        public Builder typeConvert(@NotNull ITypeConvert typeConvert) {\n            this.dataSourceConfig.typeConvert = typeConvert;\n            return this;\n        }\n\n        /**\n         * 设置数据库关键字处理器\n         *\n         * @param keyWordsHandler 关键字处理器\n         * @return this\n         */\n        public Builder keyWordsHandler(@NotNull IKeyWordsHandler keyWordsHandler) {\n            this.dataSourceConfig.keyWordsHandler = keyWordsHandler;\n            return this;\n        }\n\n        /**\n         * 指定数据库查询方式\n         *\n         * @param databaseQueryClass 查询类\n         * @return this\n         * @since 3.5.3\n         */\n        public Builder databaseQueryClass(@NotNull Class<? extends AbstractDatabaseQuery> databaseQueryClass) {\n            this.dataSourceConfig.databaseQueryClass = databaseQueryClass;\n            return this;\n        }\n\n        /**\n         * 指定类型转换器\n         *\n         * @param typeConvertHandler 类型转换器\n         * @return this\n         * @since 3.5.3\n         */\n        public Builder typeConvertHandler(@NotNull ITypeConvertHandler typeConvertHandler) {\n            this.dataSourceConfig.typeConvertHandler = typeConvertHandler;\n            return this;\n        }\n\n        /**\n         * 增加数据库连接属性\n         *\n         * @param key   属性名\n         * @param value 属性值\n         * @return this\n         * @since 3.5.3\n         */\n        public Builder addConnectionProperty(@NotNull String key, @NotNull String value) {\n            this.dataSourceConfig.connectionProperties.put(key, value);\n            return this;\n        }\n\n        /**\n         * 指定连接驱动\n         * <li>对于一些老驱动(低于4.0规范)没有实现SPI不能自动加载的,手动指定加载让其初始化注册到驱动列表去.</li>\n         *\n         * @param className 驱动全类名\n         * @return this\n         * @since 3.5.8\n         */\n        public Builder driverClassName(@NotNull String className) {\n            ClassUtils.toClassConfident(className);\n            this.dataSourceConfig.driverClassName = className;\n            return this;\n        }\n\n        /**\n         * 构建数据库配置\n         *\n         * @return 数据库配置\n         */\n        @Override\n        public DataSourceConfig build() {\n            return this.dataSourceConfig;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/GlobalConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.Service;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.function.Supplier;\n\n\n/**\n * 全局配置\n *\n * @author hubin\n * @since 2016-12-02\n */\npublic class GlobalConfig {\n\n    private GlobalConfig() {\n    }\n\n    protected static final Logger LOGGER = LoggerFactory.getLogger(GlobalConfig.class);\n\n    /**\n     * 生成文件的输出目录【 windows:D://  linux or mac:/tmp 】\n     */\n    @Getter\n    private String outputDir = System.getProperty(\"os.name\").toLowerCase().contains(\"windows\") ? \"D://\" : \"/tmp\";\n\n    /**\n     * 是否打开输出目录\n     */\n    @Getter\n    private boolean open = true;\n\n    /**\n     * 作者\n     */\n    @Getter\n    private String author = \"baomidou\";\n\n    /**\n     * 开启 Kotlin 模式（默认 false）\n     */\n    @Getter\n    private boolean kotlin;\n\n    /**\n     * 开启 swagger 模式（默认 false 与 springdoc 不可同时使用）\n     */\n    private boolean swagger;\n    /**\n     * 开启 springdoc 模式（默认 false 与 swagger 不可同时使用）\n     */\n    @Getter\n    private boolean springdoc;\n\n    /**\n     * 时间类型对应策略\n     */\n    private DateType dateType = DateType.TIME_PACK;\n\n    /**\n     * 获取注释日期\n     *\n     * @since 3.5.0\n     */\n    private Supplier<String> commentDate = () -> new SimpleDateFormat(\"yyyy-MM-dd\").format(new Date());\n\n    /**\n     * 是否生成service 接口（默认 true）\n     * 增加此开关的原因：在某些项目实践中，只需要生成service实现类，不需要抽象sevice接口\n     * 针对某些项目，生成service接口，开发时反而麻烦，这种情况，可以将该属性设置为false\n     * @deprecated 3.5.6 {@link Service.Builder#disableService()}\n     */\n    @Getter\n    @Setter\n    @Deprecated\n    private boolean serviceInterface = true;\n\n    public boolean isSwagger() {\n        // springdoc 设置优先于 swagger\n        return !springdoc && swagger;\n    }\n\n    @NotNull\n    public DateType getDateType() {\n        return dateType;\n    }\n\n    @NotNull\n    public String getCommentDate() {\n        return commentDate.get();\n    }\n\n\n    /**\n     * 全局配置构建\n     *\n     * @author nieqiurong 2020/10/11.\n     * @since 3.5.0\n     */\n    public static class Builder implements IConfigBuilder<GlobalConfig> {\n\n        private final GlobalConfig globalConfig;\n\n        public Builder() {\n            this.globalConfig = new GlobalConfig();\n        }\n\n        /**\n         * 禁止打开输出目录\n         */\n        public Builder disableOpenDir() {\n            this.globalConfig.open = false;\n            return this;\n        }\n\n        /**\n         * 输出目录\n         */\n        public Builder outputDir(@NotNull String outputDir) {\n            this.globalConfig.outputDir = outputDir;\n            return this;\n        }\n\n        /**\n         * 作者\n         */\n        public Builder author(@NotNull String author) {\n            this.globalConfig.author = author;\n            return this;\n        }\n\n        /**\n         * 开启 kotlin 模式\n         */\n        public Builder enableKotlin() {\n            this.globalConfig.kotlin = true;\n            return this;\n        }\n\n        /**\n         * 开启 swagger 模式\n         */\n        public Builder enableSwagger() {\n            this.globalConfig.swagger = true;\n            return this;\n        }\n\n        /**\n         * 开启 springdoc 模式\n         */\n        public Builder enableSpringdoc() {\n            this.globalConfig.springdoc = true;\n            return this;\n        }\n\n        /**\n         * 不生成service接口\n         */\n        public Builder disableServiceInterface() {\n            this.globalConfig.serviceInterface = false;\n            return this;\n        }\n\n        /**\n         * 时间类型对应策略\n         */\n        public Builder dateType(@NotNull DateType dateType) {\n            this.globalConfig.dateType = dateType;\n            return this;\n        }\n\n        /**\n         * 注释日期获取处理\n         * example: () -> LocalDateTime.now().format(DateTimeFormatter.ISO_DATE)\n         *\n         * @param commentDate 获取注释日期\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder commentDate(@NotNull Supplier<String> commentDate) {\n            this.globalConfig.commentDate = commentDate;\n            return this;\n        }\n\n        /**\n         * 指定注释日期格式化\n         *\n         * @param pattern 格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder commentDate(@NotNull String pattern) {\n            return commentDate(() -> new SimpleDateFormat(pattern).format(new Date()));\n        }\n\n        @Override\n        public GlobalConfig build() {\n            return this.globalConfig;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/IConfigBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\n/**\n * 配置构建接口\n *\n * @param <T>\n */\npublic interface IConfigBuilder<T> {\n\n    T build();\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/IDbQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * 表数据查询接口\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic interface IDbQuery {\n    /**\n     * 表信息查询 SQL\n     */\n    String tablesSql();\n\n    /**\n     * 表字段信息查询 SQL\n     */\n    String tableFieldsSql();\n\n    /**\n     * 表名称\n     */\n    String tableName();\n\n    /**\n     * 表注释\n     */\n    String tableComment();\n\n    /**\n     * 字段名称\n     */\n    String fieldName();\n\n    /**\n     * 字段类型\n     */\n    String fieldType();\n\n    /**\n     * 字段注释\n     */\n    String fieldComment();\n\n    /**\n     * 主键字段\n     */\n    String fieldKey();\n\n    /**\n     * 判断主键是否为identity\n     *\n     * @param results ResultSet\n     * @return 主键是否为identity\n     * @throws SQLException ignore\n     */\n    boolean isKeyIdentity(ResultSet results) throws SQLException;\n\n    /**\n     * 自定义字段名称\n     */\n    String[] fieldCustom();\n\n    /**\n     * 获取主键sql\n     *\n     * @param dataSourceConfig 数据库配置信息\n     * @param tableName        表名\n     * @return 主键查询sql\n     * @since 3.5.7\n     */\n    default String primaryKeySql(DataSourceConfig dataSourceConfig, String tableName) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/IKeyWordsHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Collection;\n\n/**\n * 关键字处理接口\n *\n * @author nieqiurong 2020/5/7.\n * @since 3.3.2\n */\npublic interface IKeyWordsHandler {\n\n    /**\n     * 获取关键字\n     *\n     * @return 关键字集合\n     */\n    @NotNull\n    Collection<String> getKeyWords();\n\n    /**\n     * 格式化关键字格式\n     *\n     * @return 格式\n     */\n    @NotNull\n    String formatStyle();\n\n    /**\n     * 是否为关键字\n     *\n     * @param columnName 字段名称\n     * @return 是否为关键字\n     */\n    boolean isKeyWords(@NotNull String columnName);\n\n    /**\n     * 格式化字段\n     *\n     * @param columnName 字段名称\n     * @return 格式化字段\n     */\n    @NotNull\n    default String formatColumn(@NotNull String columnName) {\n        return String.format(formatStyle(), columnName);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/INameConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Set;\n\n/**\n * 名称转换接口类\n *\n * @author hubin\n * @since 2017-01-20\n */\npublic interface INameConvert {\n\n    /**\n     * 执行实体名称转换\n     *\n     * @param tableInfo 表信息对象\n     */\n    @NotNull\n    String entityNameConvert(@NotNull TableInfo tableInfo);\n\n    /**\n     * 执行属性名称转换\n     *\n     * @param field 表字段对象，如果属性表字段命名不一致注意 convert 属性的设置\n     */\n    @NotNull\n    String propertyNameConvert(@NotNull TableField field);\n\n    /**\n     * 默认名称转换接口类\n     *\n     * @author nieqiurong 2020/9/20.\n     * @since 3.5.0\n     */\n    class DefaultNameConvert implements INameConvert {\n\n        private final StrategyConfig strategyConfig;\n\n        public DefaultNameConvert(StrategyConfig strategyConfig) {\n            this.strategyConfig = strategyConfig;\n        }\n\n        @Override\n        public @NotNull String entityNameConvert(@NotNull TableInfo tableInfo) {\n            return NamingStrategy.capitalFirst(processName(tableInfo.getName(), strategyConfig.entity().getNaming(), strategyConfig.getTablePrefix(), strategyConfig.getTableSuffix()));\n        }\n\n        @Override\n        public @NotNull String propertyNameConvert(@NotNull TableField field) {\n            return processName(field.getName(), strategyConfig.entity().getColumnNaming(), strategyConfig.getFieldPrefix(), strategyConfig.getFieldSuffix());\n        }\n\n        private String processName(String name, NamingStrategy strategy, Set<String> prefix, Set<String> suffix) {\n            String propertyName = name;\n            // 删除前缀\n            if (!prefix.isEmpty()) {\n                propertyName = NamingStrategy.removePrefix(propertyName, prefix);\n            }\n            // 删除后缀\n            if (!suffix.isEmpty()) {\n                propertyName = NamingStrategy.removeSuffix(propertyName, suffix);\n            }\n            if (StringUtils.isBlank(propertyName)) {\n                throw new RuntimeException(String.format(\"%s 的名称转换结果为空，请检查是否配置问题\", name));\n            }\n            // 下划线转驼峰\n            if (NamingStrategy.underline_to_camel.equals(strategy)) {\n                return NamingStrategy.underlineToCamel(propertyName);\n            }\n            return propertyName;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/IOutputFile.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport java.io.File;\n\n/**\n * 输出文件接口\n *\n * @author hubin\n * @since 2023-08-04\n */\npublic interface IOutputFile {\n\n    /**\n     * 创建文件\n     *\n     * @param filePath   默认文件路径\n     * @param outputFile 输出文件类型\n     * @return {@link File}\n     */\n    File createFile(String filePath, OutputFile outputFile);\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/ITypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 数据库字段类型转换\n *\n * @author hubin\n * @author hanchunlin\n * @since 2017-01-20\n */\npublic interface ITypeConvert {\n\n    /**\n     * 执行类型转换\n     *\n     * @param globalConfig 全局配置\n     * @param tableField   字段列信息\n     * @return ignore\n     */\n    default IColumnType processTypeConvert(@NotNull GlobalConfig globalConfig, @NotNull TableField tableField) {\n        return processTypeConvert(globalConfig, tableField.getType());\n    }\n\n    /**\n     * 执行类型转换\n     *\n     * @param globalConfig 全局配置\n     * @param fieldType    字段类型\n     * @return ignore\n     */\n    IColumnType processTypeConvert(@NotNull GlobalConfig globalConfig, @NotNull String fieldType);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/InjectionConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.CustomFile;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiConsumer;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\n/**\n * 注入配置\n *\n * @author hubin\n * @since 2016-12-07\n */\npublic class InjectionConfig {\n\n    private final static Logger LOGGER = LoggerFactory.getLogger(InjectionConfig.class);\n\n    /**\n     * 输出文件之前消费者\n     */\n    private BiConsumer<TableInfo, Map<String, Object>> beforeOutputFileBiConsumer;\n\n    /**\n     * 自定义配置 Map 对象\n     */\n    private Map<String, Object> customMap = new HashMap<>();\n\n    /**\n     * 自定义模板文件列表\n     *\n     * @since 3.5.3\n     */\n    private final List<CustomFile> customFiles = new ArrayList<>();\n\n    /**\n     * 输出文件前\n     */\n    public void beforeOutputFile(TableInfo tableInfo, Map<String, Object> objectMap) {\n        if (!customMap.isEmpty()) {\n            objectMap.putAll(customMap);\n            //增加一个兼容兼容取值,推荐还是直接取值外置key即可,例如abc取值${abc}而不需要${cfg.abc}\n            objectMap.put(\"cfg\", customMap);\n        }\n        if (null != beforeOutputFileBiConsumer) {\n            beforeOutputFileBiConsumer.accept(tableInfo, objectMap);\n        }\n    }\n\n    /**\n     * 获取自定义配置 Map 对象\n     */\n    @NotNull\n    public Map<String, Object> getCustomMap() {\n        return customMap;\n    }\n\n    /**\n     * 获取自定义模板文件列表\n     */\n    @NotNull\n    public List<CustomFile> getCustomFiles() {\n        return customFiles;\n    }\n\n    /**\n     * 构建者\n     */\n    public static class Builder implements IConfigBuilder<InjectionConfig> {\n\n        private final InjectionConfig injectionConfig;\n\n        public Builder() {\n            this.injectionConfig = new InjectionConfig();\n        }\n\n        /**\n         * 输出文件之前消费者\n         *\n         * @param biConsumer 消费者\n         * @return this\n         */\n        public Builder beforeOutputFile(@NotNull BiConsumer<TableInfo, Map<String, Object>> biConsumer) {\n            this.injectionConfig.beforeOutputFileBiConsumer = biConsumer;\n            return this;\n        }\n\n        /**\n         * 自定义配置 Map 对象\n         *\n         * @param customMap Map 对象\n         * @return this\n         */\n        public Builder customMap(@NotNull Map<String, Object> customMap) {\n            this.injectionConfig.customMap = customMap;\n            return this;\n        }\n\n        /**\n         * 自定义配置模板文件\n         *\n         * @param customFile key为文件名称，value为文件路径\n         * @return this\n         */\n        public Builder customFile(@NotNull Map<String, String> customFile) {\n            return customFile(customFile.entrySet().stream()\n                .map(e -> new CustomFile.Builder().fileName(e.getKey()).templatePath(e.getValue()).build())\n                .collect(Collectors.toList()));\n        }\n\n        public Builder customFile(@NotNull CustomFile customFile) {\n            this.injectionConfig.customFiles.add(customFile);\n            return this;\n        }\n\n        public Builder customFile(@NotNull List<CustomFile> customFiles) {\n            this.injectionConfig.customFiles.addAll(customFiles);\n            return this;\n        }\n\n        public Builder customFile(Consumer<CustomFile.Builder> consumer) {\n            CustomFile.Builder builder = new CustomFile.Builder();\n            consumer.accept(builder);\n            this.injectionConfig.customFiles.add(builder.build());\n            return this;\n        }\n\n        @Override\n        public InjectionConfig build() {\n            return this.injectionConfig;\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/OutputFile.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\n/**\n * 输出文件类型\n *\n * @author hubin\n * @since 2021-06-01\n */\npublic enum OutputFile {\n    entity,\n    service,\n    serviceImpl,\n    mapper,\n    xml,\n    controller,\n    parent;\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/PackageConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.builder.CustomFile;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 包相关的配置项\n *\n * @author YangHu, tangguo, hubin\n * @since 2016-08-30\n */\npublic class PackageConfig {\n\n    private PackageConfig() {\n    }\n\n    /**\n     * 父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名\n     */\n    private String parent = \"com.baomidou\";\n\n    /**\n     * 父包模块名\n     */\n    @Getter\n    private String moduleName = \"\";\n\n    /**\n     * Entity包名\n     */\n    @Getter\n    private String entity = \"entity\";\n\n    /**\n     * Service包名\n     */\n    @Getter\n    private String service = \"service\";\n\n    /**\n     * Service Impl包名\n     */\n    @Getter\n    private String serviceImpl = \"service.impl\";\n\n    /**\n     * Mapper包名\n     */\n    @Getter\n    private String mapper = \"mapper\";\n\n    /**\n     * Mapper XML包名\n     */\n    @Getter\n    private String xml = \"mapper.xml\";\n\n    /**\n     * Controller包名\n     */\n    @Getter\n    private String controller = \"controller\";\n\n    /**\n     * 路径配置信息\n     */\n    @Getter\n    private Map<OutputFile, String> pathInfo;\n\n    /**\n     * 包配置信息\n     *\n     * @since 3.5.0\n     */\n    private final Map<String, String> packageInfo = new HashMap<>();\n\n    /**\n     * 父包名\n     */\n    @NotNull\n    public String getParent() {\n        if (StringUtils.isNotBlank(moduleName)) {\n            return parent + StringPool.DOT + moduleName;\n        }\n        return parent;\n    }\n\n    /**\n     * 连接父子包名\n     *\n     * @param subPackage 子包名\n     * @return 连接后的包名\n     */\n    @NotNull\n    public String joinPackage(String subPackage) {\n        String parent = getParent();\n        return StringUtils.isBlank(parent) ? subPackage : (parent + (StringUtils.isBlank(subPackage) ? StringPool.EMPTY : StringPool.DOT + subPackage));\n    }\n\n    /**\n     * 获取包配置信息\n     *\n     * @return 包配置信息\n     * @see #getPackageInfo(InjectionConfig)\n     * @since 3.5.0\n     * @deprecated 3.5.10\n     */\n    @NotNull\n    @Deprecated\n    public Map<String, String> getPackageInfo() {\n        return getPackageInfo((InjectionConfig) null);\n    }\n\n    /**\n     * 获取包配置信息\n     *\n     * @param injectionConfig 配置文件信息\n     * @return 包配置信息\n     * @since 3.5.10\n     */\n    @NotNull\n    public Map<String, String> getPackageInfo(InjectionConfig injectionConfig) {\n        if (packageInfo.isEmpty()) {\n            packageInfo.put(ConstVal.MODULE_NAME, this.getModuleName());\n            packageInfo.put(ConstVal.ENTITY, this.joinPackage(this.getEntity()));\n            packageInfo.put(ConstVal.MAPPER, this.joinPackage(this.getMapper()));\n            packageInfo.put(ConstVal.XML, this.joinPackage(this.getXml()));\n            packageInfo.put(ConstVal.SERVICE, this.joinPackage(this.getService()));\n            packageInfo.put(ConstVal.SERVICE_IMPL, this.joinPackage(this.getServiceImpl()));\n            packageInfo.put(ConstVal.CONTROLLER, this.joinPackage(this.getController()));\n            packageInfo.put(ConstVal.PARENT, this.getParent());\n            if (injectionConfig != null) {\n                List<CustomFile> customFiles = injectionConfig.getCustomFiles();\n                for (CustomFile customFile : customFiles) {\n                    if (StringUtils.isNotBlank(customFile.getPackageName())) {\n                        String name = customFile.getShortName();\n                        if (StringUtils.isNotBlank(this.parent)\n                            && customFile.getPackageName().startsWith(this.parent)) {\n                            packageInfo.put(name, customFile.getPackageName());\n                        } else {\n                            packageInfo.put(name, this.joinPackage(customFile.getPackageName()));\n                        }\n                    }\n                }\n            }\n        }\n        return Collections.unmodifiableMap(this.packageInfo);\n    }\n\n    /**\n     * 获取包配置信息\n     *\n     * @param module 模块\n     * @return 配置信息\n     * @see #getPackageInfo(InjectionConfig, String)\n     * @since 3.5.0\n     */\n    @Deprecated\n    public String getPackageInfo(String module) {\n        return getPackageInfo().get(module);\n    }\n\n    /**\n     * @since 3.5.10\n     * @param injectionConfig 注入配置\n     * @param module 模块\n     * @return 配置信息\n     */\n    public String getPackageInfo(InjectionConfig injectionConfig, String module) {\n        return getPackageInfo(injectionConfig).get(module);\n    }\n\n    /**\n     * 构建者\n     *\n     * @author nieqiurong\n     * @since 3.5.0\n     */\n    public static class Builder implements IConfigBuilder<PackageConfig> {\n\n        private final PackageConfig packageConfig;\n\n        public Builder() {\n            this.packageConfig = new PackageConfig();\n        }\n\n        public Builder(@NotNull String parent, @NotNull String moduleName) {\n            this();\n            this.packageConfig.parent = parent;\n            this.packageConfig.moduleName = moduleName;\n        }\n\n        /**\n         * 指定父包名\n         *\n         * @param parent 父包名\n         * @return this\n         */\n        public Builder parent(@NotNull String parent) {\n            this.packageConfig.parent = parent;\n            return this;\n        }\n\n        /**\n         * 指定模块名称\n         *\n         * @param moduleName 模块名\n         * @return this\n         */\n        public Builder moduleName(@NotNull String moduleName) {\n            this.packageConfig.moduleName = moduleName;\n            return this;\n        }\n\n        /**\n         * 指定实体包名\n         *\n         * @param entity 实体包名\n         * @return this\n         */\n        public Builder entity(@NotNull String entity) {\n            this.packageConfig.entity = entity;\n            return this;\n        }\n\n        /**\n         * 指定service接口包名\n         *\n         * @param service service包名\n         * @return this\n         */\n        public Builder service(@NotNull String service) {\n            this.packageConfig.service = service;\n            return this;\n        }\n\n        /**\n         * service实现类包名\n         *\n         * @param serviceImpl service实现类包名\n         * @return this\n         */\n        public Builder serviceImpl(@NotNull String serviceImpl) {\n            this.packageConfig.serviceImpl = serviceImpl;\n            return this;\n        }\n\n        /**\n         * 指定mapper接口包名\n         *\n         * @param mapper mapper包名\n         * @return this\n         */\n        public Builder mapper(@NotNull String mapper) {\n            this.packageConfig.mapper = mapper;\n            return this;\n        }\n\n        /**\n         * 指定xml包名\n         *\n         * @param xml xml包名\n         * @return this\n         */\n        public Builder xml(@NotNull String xml) {\n            this.packageConfig.xml = xml;\n            return this;\n        }\n\n        /**\n         * 指定控制器包名\n         *\n         * @param controller 控制器包名\n         * @return this\n         */\n        public Builder controller(@NotNull String controller) {\n            this.packageConfig.controller = controller;\n            return this;\n        }\n\n        /**\n         * 路径配置信息\n         *\n         * @param pathInfo 路径配置信息\n         * @return this\n         */\n        public Builder pathInfo(@NotNull Map<OutputFile, String> pathInfo) {\n            this.packageConfig.pathInfo = pathInfo;\n            return this;\n        }\n\n        /**\n         * 连接父子包名\n         *\n         * @param subPackage 子包名\n         * @return 连接后的包名\n         */\n        @NotNull\n        public String joinPackage(@NotNull String subPackage) {\n            return this.packageConfig.joinPackage(subPackage);\n        }\n\n        /**\n         * 构建包配置对象\n         * <p>当指定{@link #parent(String)} 与 {@link #moduleName(String)}时,其他模块名字会加上这两个作为前缀</p>\n         * <p>\n         * 例如:\n         * <p>当设置 {@link #parent(String)},那么entity的配置为 {@link #getParent()}.{@link #getEntity()}</p>\n         * <p>当设置 {@link #parent(String)}与{@link #moduleName(String)},那么entity的配置为 {@link #getParent()}.{@link #getModuleName()}.{@link #getEntity()} </p>\n         * </p>\n         *\n         * @return 包配置对象\n         */\n        @Override\n        public PackageConfig build() {\n            return this.packageConfig;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/StrategyConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.builder.*;\nimport com.baomidou.mybatisplus.generator.config.po.LikeTable;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 策略配置项\n *\n * @author YangHu, tangguo, hubin\n * @since 2016/8/30\n */\npublic class StrategyConfig {\n\n    private StrategyConfig() {\n    }\n\n    /**\n     * 是否大写命名（默认 false）\n     */\n    private boolean isCapitalMode;\n\n    /**\n     * 是否跳过视图（默认 false）\n     */\n    @Getter\n    private boolean skipView;\n\n    /**\n     * 过滤表前缀\n     * example: addTablePrefix(\"t_\")\n     * result: t_simple -> Simple\n     */\n    private final Set<String> tablePrefix = new HashSet<>();\n\n    /**\n     * 过滤表后缀\n     * example: addTableSuffix(\"_0\")\n     * result: t_simple_0 -> Simple\n     */\n    private final Set<String> tableSuffix = new HashSet<>();\n\n    /**\n     * 过滤字段前缀\n     * example: addFieldPrefix(\"is_\")\n     * result: is_deleted -> deleted\n     */\n    private final Set<String> fieldPrefix = new HashSet<>();\n\n    /**\n     * 过滤字段后缀\n     * example: addFieldSuffix(\"_flag\")\n     * result: deleted_flag -> deleted\n     */\n    private final Set<String> fieldSuffix = new HashSet<>();\n\n    /**\n     * 需要包含的表名，允许正则表达式（与exclude二选一配置）<br/>\n     * 当{@link #enableSqlFilter}为true时，正则表达式无效.\n     */\n    private final Set<String> include = new HashSet<>();\n\n    /**\n     * 需要排除的表名，允许正则表达式<br/>\n     * 当{@link #enableSqlFilter}为true时，正则表达式无效.\n     */\n    private final Set<String> exclude = new HashSet<>();\n\n    /**\n     * 启用sql过滤，语法不能支持使用sql过滤表的话，可以考虑关闭此开关.\n     *\n     * @since 3.3.1\n     */\n    @Getter\n    private boolean enableSqlFilter = true;\n\n    /**\n     * 启用 schema 默认 false\n     */\n    @Getter\n    private boolean enableSchema;\n\n    /**\n     * 包含表名\n     *\n     * @since 3.3.0\n     */\n    private LikeTable likeTable;\n\n    /**\n     * 不包含表名\n     * <p>\n     * 只在{@link com.baomidou.mybatisplus.generator.query.SQLQuery}模式下生效.\n     * </p>\n     *\n     * @since 3.3.0\n     */\n    private LikeTable notLikeTable;\n\n    private final Entity.Builder entityBuilder = new Entity.Builder(this);\n\n    private final Controller.Builder controllerBuilder = new Controller.Builder(this);\n\n    private final Mapper.Builder mapperBuilder = new Mapper.Builder(this);\n\n    private final Service.Builder serviceBuilder = new Service.Builder(this);\n\n    private Entity entity;\n\n    private Controller controller;\n\n    private Mapper mapper;\n\n    private Service service;\n\n    private IOutputFile outputFile = (path, ot) -> new File(path);\n\n    /**\n     * 实体配置构建者\n     *\n     * @return 实体配置构建者\n     * @since 3.5.0\n     */\n    @NotNull\n    public Entity.Builder entityBuilder() {\n        return entityBuilder;\n    }\n\n    /**\n     * 实体配置\n     *\n     * @return 实体配置\n     * @since 3.5.0\n     */\n    @NotNull\n    public Entity entity() {\n        if (entity == null) {\n            this.entity = entityBuilder.get();\n        }\n        return entity;\n    }\n\n    /**\n     * 控制器配置构建者\n     *\n     * @return 控制器配置构建者\n     * @since 3.5.0\n     */\n    @NotNull\n    public Controller.Builder controllerBuilder() {\n        return controllerBuilder;\n    }\n\n    /**\n     * 控制器配置\n     *\n     * @return 控制器配置\n     * @since 3.5.0\n     */\n    @NotNull\n    public Controller controller() {\n        if (controller == null) {\n            this.controller = controllerBuilder.get();\n        }\n        return controller;\n    }\n\n    /**\n     * Mapper配置构建者\n     *\n     * @return Mapper配置构建者\n     * @since 3.5.0\n     */\n    @NotNull\n    public Mapper.Builder mapperBuilder() {\n        return mapperBuilder;\n    }\n\n    /**\n     * Mapper配置\n     *\n     * @return Mapper配置\n     * @since 3.5.0\n     */\n    @NotNull\n    public Mapper mapper() {\n        if (mapper == null) {\n            this.mapper = mapperBuilder.get();\n        }\n        return mapper;\n    }\n\n    /**\n     * Service配置构建者\n     *\n     * @return Service配置构建者\n     * @since 3.5.0\n     */\n    @NotNull\n    public Service.Builder serviceBuilder() {\n        return serviceBuilder;\n    }\n\n    /**\n     * Service配置\n     *\n     * @return Service配置\n     * @since 3.5.0\n     */\n    @NotNull\n    public Service service() {\n        if (service == null) {\n            this.service = serviceBuilder.get();\n        }\n        return service;\n    }\n\n    /**\n     * 大写命名、字段符合大写字母数字下划线命名\n     *\n     * @param word 待判断字符串\n     */\n    public boolean isCapitalModeNaming(@NotNull String word) {\n        return isCapitalMode && StringUtils.isCapitalMode(word);\n    }\n\n    /**\n     * 表名称匹配过滤表前缀\n     *\n     * @param tableName 表名称\n     * @since 3.3.2\n     */\n    public boolean startsWithTablePrefix(@NotNull String tableName) {\n        return this.tablePrefix.stream().anyMatch(tableName::startsWith);\n    }\n\n    /**\n     * 验证配置项\n     *\n     * @since 3.5.0\n     */\n    public void validate() {\n        boolean isInclude = !this.getInclude().isEmpty();\n        boolean isExclude = !this.getExclude().isEmpty();\n        if (isInclude && isExclude) {\n            throw new IllegalArgumentException(\"`include` and `exclude` configurations are mutually exclusive and cannot be used simultaneously.\");\n        }\n        if (this.getNotLikeTable() != null && this.getLikeTable() != null) {\n            throw new IllegalArgumentException(\"`likeTable` and `notLikeTable` configurations are mutually exclusive and cannot be used simultaneously.\");\n        }\n    }\n\n    /**\n     * 包含表名匹配\n     *\n     * @param tableName 表名\n     * @return 是否匹配\n     * @since 3.5.0\n     */\n    public boolean matchIncludeTable(@NotNull String tableName) {\n        return matchTable(tableName, this.getInclude());\n    }\n\n    /**\n     * 排除表名匹配\n     *\n     * @param tableName 表名\n     * @return 是否匹配\n     * @since 3.5.0\n     */\n    public boolean matchExcludeTable(@NotNull String tableName) {\n        return matchTable(tableName, this.getExclude());\n    }\n\n    /**\n     * 表名匹配\n     *\n     * @param tableName   表名\n     * @param matchTables 匹配集合\n     * @return 是否匹配\n     * @since 3.5.0\n     */\n    private boolean matchTable(@NotNull String tableName, @NotNull Set<String> matchTables) {\n        return matchTables.stream().anyMatch(t -> tableNameMatches(t, tableName));\n    }\n\n    /**\n     * 表名匹配\n     *\n     * @param matchTableName 匹配表名\n     * @param dbTableName    数据库表名\n     * @return 是否匹配\n     */\n    private boolean tableNameMatches(@NotNull String matchTableName, @NotNull String dbTableName) {\n        return matchTableName.equalsIgnoreCase(dbTableName) || StringUtils.matches(matchTableName, dbTableName);\n    }\n\n    public boolean isCapitalMode() {\n        return isCapitalMode;\n    }\n\n    @NotNull\n    public Set<String> getTablePrefix() {\n        return tablePrefix;\n    }\n\n    @NotNull\n    public Set<String> getTableSuffix() {\n        return tableSuffix;\n    }\n\n    @NotNull\n    public Set<String> getFieldPrefix() {\n        return fieldPrefix;\n    }\n\n    @NotNull\n    public Set<String> getFieldSuffix() {\n        return fieldSuffix;\n    }\n\n    @NotNull\n    public Set<String> getInclude() {\n        return include;\n    }\n\n    @NotNull\n    public Set<String> getExclude() {\n        return exclude;\n    }\n\n    @Nullable\n    public LikeTable getLikeTable() {\n        return likeTable;\n    }\n\n    @Nullable\n    public LikeTable getNotLikeTable() {\n        return notLikeTable;\n    }\n\n    @NotNull\n    public IOutputFile getOutputFile() {\n        return outputFile;\n    }\n\n    /**\n     * 策略配置构建者\n     *\n     * @author nieqiurong 2020/10/11.\n     * @since 3.5.0\n     */\n    public static class Builder extends BaseBuilder {\n\n        private final StrategyConfig strategyConfig;\n\n        public Builder() {\n            super(new StrategyConfig());\n            strategyConfig = super.build();\n        }\n\n        /**\n         * 开启大写命名\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableCapitalMode() {\n            this.strategyConfig.isCapitalMode = true;\n            return this;\n        }\n\n        /**\n         * 开启跳过视图\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableSkipView() {\n            this.strategyConfig.skipView = true;\n            return this;\n        }\n\n        /**\n         * 禁用sql过滤\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder disableSqlFilter() {\n            this.strategyConfig.enableSqlFilter = false;\n            return this;\n        }\n\n        /**\n         * 启用 schema\n         *\n         * @return this\n         * @since 3.5.1\n         */\n        public Builder enableSchema() {\n            this.strategyConfig.enableSchema = true;\n            return this;\n        }\n\n        /**\n         * 增加过滤表前缀\n         *\n         * @param tablePrefix 过滤表前缀\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addTablePrefix(@NotNull String... tablePrefix) {\n            return addTablePrefix(Arrays.asList(tablePrefix));\n        }\n\n        public Builder addTablePrefix(@NotNull List<String> tablePrefixList) {\n            this.strategyConfig.tablePrefix.addAll(tablePrefixList);\n            return this;\n        }\n\n        /**\n         * 增加过滤表后缀\n         *\n         * @param tableSuffix 过滤表后缀\n         * @return this\n         * @since 3.5.1\n         */\n        public Builder addTableSuffix(String... tableSuffix) {\n            return addTableSuffix(Arrays.asList(tableSuffix));\n        }\n\n        public Builder addTableSuffix(@NotNull List<String> tableSuffixList) {\n            this.strategyConfig.tableSuffix.addAll(tableSuffixList);\n            return this;\n        }\n\n        /**\n         * 增加过滤字段前缀\n         *\n         * @param fieldPrefix 过滤字段前缀\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addFieldPrefix(@NotNull String... fieldPrefix) {\n            return addFieldPrefix(Arrays.asList(fieldPrefix));\n        }\n\n        public Builder addFieldPrefix(@NotNull List<String> fieldPrefix) {\n            this.strategyConfig.fieldPrefix.addAll(fieldPrefix);\n            return this;\n        }\n\n        /**\n         * 增加过滤字段后缀\n         *\n         * @param fieldSuffix 过滤字段后缀\n         * @return this\n         * @since 3.5.1\n         */\n        public Builder addFieldSuffix(@NotNull String... fieldSuffix) {\n            return addFieldSuffix(Arrays.asList(fieldSuffix));\n        }\n\n        public Builder addFieldSuffix(@NotNull List<String> fieldSuffixList) {\n            this.strategyConfig.fieldSuffix.addAll(fieldSuffixList);\n            return this;\n        }\n\n        /**\n         * 增加包含的表名\n         *\n         * @param include 包含表\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addInclude(@NotNull String... include) {\n            this.strategyConfig.include.addAll(Arrays.asList(include));\n            return this;\n        }\n\n        public Builder addInclude(@NotNull List<String> includes) {\n            this.strategyConfig.include.addAll(includes);\n            return this;\n        }\n\n        public Builder addInclude(@NotNull String include) {\n            this.strategyConfig.include.addAll(Arrays.asList(include.split(\",\")));\n            return this;\n        }\n\n        /**\n         * 增加排除表\n         *\n         * @param exclude 排除表\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addExclude(@NotNull String... exclude) {\n            return addExclude(Arrays.asList(exclude));\n        }\n\n        public Builder addExclude(@NotNull List<String> excludeList) {\n            this.strategyConfig.exclude.addAll(excludeList);\n            return this;\n        }\n\n        /**\n         * 包含表名\n         *\n         * @return this\n         */\n        public Builder likeTable(@NotNull LikeTable likeTable) {\n            this.strategyConfig.likeTable = likeTable;\n            return this;\n        }\n\n        /**\n         * 不包含表名\n         *\n         * @return this\n         */\n        public Builder notLikeTable(@NotNull LikeTable notLikeTable) {\n            this.strategyConfig.notLikeTable = notLikeTable;\n            return this;\n        }\n\n        /**\n         * 输出文件处理\n         *\n         * @return this\n         */\n        public Builder outputFile(@NotNull IOutputFile outputFile) {\n            this.strategyConfig.outputFile = outputFile;\n            return this;\n        }\n\n        @Override\n        @NotNull\n        public StrategyConfig build() {\n            this.strategyConfig.validate();\n            return strategyConfig;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/TemplateConfig.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.builder.Controller;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.builder.Service;\nimport com.baomidou.mybatisplus.generator.config.builder.Mapper;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 模板路径配置项\n *\n * @author tzg hubin\n * @see StrategyConfig.Builder#entityBuilder()\n * @see StrategyConfig.Builder#serviceBuilder()\n * @see StrategyConfig.Builder#mapperBuilder()\n * @see StrategyConfig.Builder#controllerBuilder()\n * @since 2017-06-17\n * @deprecated 3.5.6 {@link StrategyConfig}\n */\n@Deprecated\npublic class TemplateConfig {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(TemplateConfig.class);\n\n    /**\n     * 设置实体模板路径\n     */\n    private String entity;\n\n    /**\n     * 设置实体模板路径(kotlin模板)\n     */\n    private String entityKt;\n\n    /**\n     * 设置控制器模板路径\n     */\n    @Getter\n    private String controller;\n\n    /**\n     * 设置Mapper模板路径\n     */\n    @Getter\n    private String mapper;\n\n    /**\n     * 设置MapperXml模板路径\n     */\n    @Getter\n    private String xml;\n\n    /**\n     * 设置Service模板路径\n     */\n    @Getter\n    private String service;\n\n    /**\n     * 设置ServiceImpl模板路径\n     */\n    @Getter\n    private String serviceImpl;\n\n    /**\n     * 是否禁用实体模板（默认 false）\n     */\n    private boolean disableEntity;\n\n    /**\n     * 不对外爆露\n     */\n    private TemplateConfig() {\n        this.entity = ConstVal.TEMPLATE_ENTITY_JAVA;\n        this.entityKt = ConstVal.TEMPLATE_ENTITY_KT;\n        this.controller = ConstVal.TEMPLATE_CONTROLLER;\n        this.mapper = ConstVal.TEMPLATE_MAPPER;\n        this.xml = ConstVal.TEMPLATE_XML;\n        this.service = ConstVal.TEMPLATE_SERVICE;\n        this.serviceImpl = ConstVal.TEMPLATE_SERVICE_IMPL;\n    }\n\n    /**\n     * 当模板赋值为空时进行日志提示打印\n     *\n     * @param value        模板值\n     * @param templateType 模板类型\n     */\n    private void logger(String value, TemplateType templateType) {\n        if (StringUtils.isBlank(value)) {\n            LOGGER.warn(\"推荐使用disable(TemplateType.{})方法进行默认模板禁用.\", templateType.name());\n        }\n    }\n\n    /**\n     * 获取实体模板路径\n     *\n     * @param kotlin 是否kotlin\n     * @return 模板路径\n     */\n    public String getEntity(boolean kotlin) {\n        if (!this.disableEntity) {\n            if (kotlin) {\n                return StringUtils.isBlank(this.entityKt) ? ConstVal.TEMPLATE_ENTITY_KT : this.entityKt;\n            }\n            return StringUtils.isBlank(this.entity) ? ConstVal.TEMPLATE_ENTITY_JAVA : this.entity;\n        }\n        return null;\n    }\n\n    /**\n     * 禁用模板\n     *\n     * @param templateTypes 模板类型\n     * @return this\n     * @see Entity.Builder#disable()\n     * @see Service.Builder#disable()\n     * @see Service.Builder#disableService()\n     * @see Service.Builder#disableServiceImpl()\n     * @see Controller.Builder#disable()\n     * @see Mapper.Builder#disable()\n     * @see Mapper.Builder#disableMapper()\n     * @see Mapper.Builder#disableMapperXml()\n     * @since 3.3.2\n     * @deprecated 3.5.6\n     */\n    @Deprecated\n    public TemplateConfig disable(@NotNull TemplateType... templateTypes) {\n        if (templateTypes != null) {\n            for (TemplateType templateType : templateTypes) {\n                switch (templateType) {\n                    case ENTITY:\n                        this.entity = null;\n                        this.entityKt = null;\n                        //暂时没其他多的需求,使用一个单独的boolean变量进行支持一下.\n                        this.disableEntity = true;\n                        break;\n                    case CONTROLLER:\n                        this.controller = null;\n                        break;\n                    case MAPPER:\n                        this.mapper = null;\n                        break;\n                    case XML:\n                        this.xml = null;\n                        break;\n                    case SERVICE:\n                        this.service = null;\n                        break;\n                    case SERVICE_IMPL:\n                        this.serviceImpl = null;\n                        break;\n                    default:\n                }\n            }\n        }\n        return this;\n    }\n\n    /**\n     * 禁用全部模板\n     *\n     * @return this\n     * @see Entity.Builder#disable()\n     * @see Service.Builder#disable()\n     * @see Service.Builder#disableService()\n     * @see Service.Builder#disableServiceImpl()\n     * @see Controller.Builder#disable()\n     * @see Mapper.Builder#disable()\n     * @see Mapper.Builder#disableMapper()\n     * @see Mapper.Builder#disableMapperXml()\n     * @since 3.5.0\n     * @deprecated 3.5.6\n     */\n    @Deprecated\n    public TemplateConfig disable() {\n        return disable(TemplateType.values());\n    }\n\n    /**\n     * 模板路径配置构建者\n     *\n     * @author nieqiurong 3.5.0\n     * @deprecated 3.5.6 {@link StrategyConfig}\n     */\n    @Deprecated\n    public static class Builder implements IConfigBuilder<TemplateConfig> {\n\n        private final TemplateConfig templateConfig;\n\n        /**\n         * 默认生成一个空的\n         */\n        public Builder() {\n            this.templateConfig = new TemplateConfig();\n        }\n\n        /**\n         * 禁用所有模板\n         *\n         * @return this\n         */\n        public Builder disable() {\n            this.templateConfig.disable();\n            return this;\n        }\n\n        /**\n         * 禁用模板\n         *\n         * @return this\n         */\n        public Builder disable(@NotNull TemplateType... templateTypes) {\n            this.templateConfig.disable(templateTypes);\n            return this;\n        }\n\n        /**\n         * 设置实体模板路径(JAVA)\n         *\n         * @param entityTemplate 实体模板\n         * @return this\n         * @deprecated {@link Entity.Builder#javaTemplate}\n         */\n        @Deprecated\n        public Builder entity(@NotNull String entityTemplate) {\n            this.templateConfig.disableEntity = false;\n            this.templateConfig.entity = entityTemplate;\n            return this;\n        }\n\n        /**\n         * 设置实体模板路径(kotlin)\n         *\n         * @param entityKtTemplate 实体模板\n         * @return this\n         * @deprecated {@link Entity.Builder#javaTemplate}\n         */\n        @Deprecated\n        public Builder entityKt(@NotNull String entityKtTemplate) {\n            this.templateConfig.disableEntity = false;\n            this.templateConfig.entityKt = entityKtTemplate;\n            return this;\n        }\n\n        /**\n         * 设置service模板路径\n         *\n         * @param serviceTemplate service接口模板路径\n         * @return this\n         * @deprecated {@link Service.Builder#serviceTemplate(String)}\n         */\n        @Deprecated\n        public Builder service(@NotNull String serviceTemplate) {\n            this.templateConfig.service = serviceTemplate;\n            return this;\n        }\n\n        /**\n         * 设置serviceImpl模板路径\n         *\n         * @param serviceImplTemplate service实现类模板路径\n         * @return this\n         * @deprecated {@link Service.Builder#serviceImplTemplate(String)}\n         */\n        @Deprecated\n        public Builder serviceImpl(@NotNull String serviceImplTemplate) {\n            this.templateConfig.serviceImpl = serviceImplTemplate;\n            return this;\n        }\n\n        /**\n         * 设置mapper模板路径\n         *\n         * @param mapperTemplate mapper模板路径\n         * @return this\n         * @deprecated {@link Mapper.Builder#mapperTemplate(String)}\n         */\n        @Deprecated\n        public Builder mapper(@NotNull String mapperTemplate) {\n            this.templateConfig.mapper = mapperTemplate;\n            return this;\n        }\n\n        /**\n         * 设置mapperXml模板路径\n         *\n         * @param xmlTemplate xml模板路径\n         * @return this\n         * @deprecated {@link Mapper.Builder#mapperXmlTemplate(String)}\n         */\n        @Deprecated\n        public Builder xml(@NotNull String xmlTemplate) {\n            this.templateConfig.xml = xmlTemplate;\n            return this;\n        }\n\n        /**\n         * 设置控制器模板路径\n         *\n         * @param controllerTemplate 控制器模板路径\n         * @return this\n         * @deprecated {@link Controller.Builder#template(String)}\n         */\n        @Deprecated\n        public Builder controller(@NotNull String controllerTemplate) {\n            this.templateConfig.controller = controllerTemplate;\n            return this;\n        }\n\n        /**\n         * 构建模板配置对象\n         *\n         * @return 模板配置对象\n         */\n        @Override\n        public TemplateConfig build() {\n            return this.templateConfig;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/TemplateLoadWay.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\nimport lombok.Getter;\n\n/**\n * 模板文件加载方式\n *\n * @author hubin\n * @since 3.5.9\n */\n@Getter\npublic enum TemplateLoadWay {\n    STRING(\"string\"),\n    FILE(\"file\");\n\n    private final String value;\n\n    TemplateLoadWay(String value) {\n        this.value = value;\n    }\n\n    public boolean isFile() {\n        return FILE == this;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/TemplateType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config;\n\n/**\n * 模板类型\n *\n * @author nieqiurong 2020/4/28.\n * @since 3.3.2\n */\npublic enum TemplateType {\n\n    ENTITY, SERVICE, SERVICE_IMPL, CONTROLLER, MAPPER, XML\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/BaseBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.config.IConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 配置构建\n *\n * @author nieqiurong 2020/10/11.\n * @since 3.5.0\n */\npublic class BaseBuilder implements IConfigBuilder<StrategyConfig> {\n\n    private final StrategyConfig strategyConfig;\n\n    public BaseBuilder(@NotNull StrategyConfig strategyConfig) {\n        this.strategyConfig = strategyConfig;\n    }\n\n    @NotNull\n    public Entity.Builder entityBuilder() {\n        return strategyConfig.entityBuilder();\n    }\n\n    @NotNull\n    public Controller.Builder controllerBuilder() {\n        return strategyConfig.controllerBuilder();\n    }\n\n    @NotNull\n    public Mapper.Builder mapperBuilder() {\n        return strategyConfig.mapperBuilder();\n    }\n\n    @NotNull\n    public Service.Builder serviceBuilder() {\n        return strategyConfig.serviceBuilder();\n    }\n\n    @NotNull\n    @Override\n    public StrategyConfig build() {\n        this.strategyConfig.validate();\n        return this.strategyConfig;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/ConfigBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.config.*;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.query.IDatabaseQuery;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.lang.reflect.Constructor;\nimport java.util.*;\nimport java.util.regex.Pattern;\n\n/**\n * 配置汇总 传递给文件生成工具\n *\n * @author YangHu, tangguo, hubin, Juzi, lanjerry\n * @since 2016-08-30\n */\npublic class ConfigBuilder {\n\n    /**\n     * 模板路径配置信息\n     *\n     * @deprecated 3.5.6\n     */\n    @Deprecated\n    private final TemplateConfig templateConfig;\n\n    /**\n     * 数据库表信息\n     */\n    private final List<TableInfo> tableInfoList = new ArrayList<>();\n\n    /**\n     * 路径配置信息\n     */\n    private final Map<OutputFile, String> pathInfo = new HashMap<>();\n\n    /**\n     * 策略配置信息\n     */\n    private StrategyConfig strategyConfig;\n\n    /**\n     * 全局配置信息\n     */\n    private GlobalConfig globalConfig;\n\n    /**\n     * 注入配置信息\n     */\n    private InjectionConfig injectionConfig;\n\n    /**\n     * 过滤正则\n     */\n    private static final Pattern REGX = Pattern.compile(\"[~!/@#$%^&*()+\\\\\\\\\\\\[\\\\]|{};:'\\\",<.>?]+\");\n\n    /**\n     * 包配置信息\n     */\n    private final PackageConfig packageConfig;\n\n    /**\n     * 数据库配置信息\n     */\n    private final DataSourceConfig dataSourceConfig;\n\n    /**\n     * 数据查询实例\n     * @since 3.5.3\n     */\n    private final IDatabaseQuery databaseQuery;\n\n    /**\n     * 资源加载器\n     * @since 3.5.9\n     */\n    @Getter\n    @Setter\n    private TemplateLoadWay templateLoadWay = TemplateLoadWay.FILE;\n\n    /**\n     * 在构造器中处理配置\n     *\n     * @param packageConfig    包配置\n     * @param dataSourceConfig 数据源配置\n     * @param strategyConfig   表配置\n     * @param templateConfig   模板配置\n     * @param globalConfig     全局配置\n     */\n    public ConfigBuilder(@Nullable PackageConfig packageConfig, @NotNull DataSourceConfig dataSourceConfig,\n                         @Nullable StrategyConfig strategyConfig, @Nullable TemplateConfig templateConfig,\n                         @Nullable GlobalConfig globalConfig, @Nullable InjectionConfig injectionConfig) {\n        this.dataSourceConfig = dataSourceConfig;\n        this.strategyConfig = Optional.ofNullable(strategyConfig).orElseGet(GeneratorBuilder::strategyConfig);\n        this.globalConfig = Optional.ofNullable(globalConfig).orElseGet(GeneratorBuilder::globalConfig);\n        this.templateConfig = Optional.ofNullable(templateConfig).orElseGet(GeneratorBuilder::templateConfig);\n        this.packageConfig = Optional.ofNullable(packageConfig).orElseGet(GeneratorBuilder::packageConfig);\n        this.injectionConfig = Optional.ofNullable(injectionConfig).orElseGet(GeneratorBuilder::injectionConfig);\n        this.pathInfo.putAll(new PathInfoHandler(this.injectionConfig, this.globalConfig, this.strategyConfig, this.packageConfig).getPathInfo());\n        Class<? extends IDatabaseQuery> databaseQueryClass = dataSourceConfig.getDatabaseQueryClass();\n        try {\n            Constructor<? extends IDatabaseQuery> declaredConstructor = databaseQueryClass.getDeclaredConstructor(this.getClass());\n            this.databaseQuery = declaredConstructor.newInstance(this);\n        } catch (ReflectiveOperationException exception) {\n            throw new RuntimeException(exception);\n        }\n    }\n\n    /**\n     * 判断表名是否为正则表名(这表名规范比较随意,只能尽量匹配上特殊符号)\n     *\n     * @param tableName 表名\n     * @return 是否正则\n     * @since 3.5.0\n     */\n    public static boolean matcherRegTable(@NotNull String tableName) {\n        return REGX.matcher(tableName).find();\n    }\n\n    @NotNull\n    public ConfigBuilder setStrategyConfig(@NotNull StrategyConfig strategyConfig) {\n        this.strategyConfig = strategyConfig;\n        return this;\n    }\n\n    @NotNull\n    public ConfigBuilder setGlobalConfig(@NotNull GlobalConfig globalConfig) {\n        this.globalConfig = globalConfig;\n        return this;\n    }\n\n    @NotNull\n    public ConfigBuilder setInjectionConfig(@NotNull InjectionConfig injectionConfig) {\n        this.injectionConfig = injectionConfig;\n        return this;\n    }\n\n    /**\n     * 获取模板配置\n     *\n     * @return 模板配置\n     * @deprecated 3.5.6 {@link #strategyConfig}\n     */\n    @NotNull\n    @Deprecated\n    public TemplateConfig getTemplateConfig() {\n        return templateConfig;\n    }\n\n    @NotNull\n    public List<TableInfo> getTableInfoList() {\n        if (tableInfoList.isEmpty()) {\n            List<TableInfo> tableInfos = this.databaseQuery.queryTables();\n            if (!tableInfos.isEmpty()) {\n                this.tableInfoList.addAll(tableInfos);\n            }\n        }\n        return tableInfoList;\n    }\n\n    @NotNull\n    public Map<OutputFile, String> getPathInfo() {\n        return pathInfo;\n    }\n\n    @NotNull\n    public StrategyConfig getStrategyConfig() {\n        return strategyConfig;\n    }\n\n    @NotNull\n    public GlobalConfig getGlobalConfig() {\n        return globalConfig;\n    }\n\n    @Nullable\n    public InjectionConfig getInjectionConfig() {\n        return injectionConfig;\n    }\n\n    @NotNull\n    public PackageConfig getPackageConfig() {\n        return packageConfig;\n    }\n\n    @NotNull\n    public DataSourceConfig getDataSourceConfig() {\n        return dataSourceConfig;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/Controller.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.ITemplate;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.function.ConverterFileName;\nimport com.baomidou.mybatisplus.generator.util.ClassUtils;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 控制器属性配置\n *\n * @author nieqiurong 2020/10/11.\n * @since 3.5.0\n */\npublic class Controller implements ITemplate {\n\n    private Controller() {\n    }\n\n    /**\n     * 生成 <code>@RestController</code> 控制器（默认 false）\n     * <pre>\n     *      <code>@Controller</code> -> <code>@RestController</code>\n     * </pre>\n     */\n    @Getter\n    private boolean restStyle;\n\n    /**\n     * 驼峰转连字符（默认 false）\n     * <pre>\n     *      <code>@RequestMapping(\"/managerUserActionHistory\")</code> -> <code>@RequestMapping(\"/manager-user-action-history\")</code>\n     * </pre>\n     */\n    @Getter\n    private boolean hyphenStyle;\n\n    /**\n     * 自定义继承的Controller类全称，带包名\n     */\n    private String superClass;\n\n    /**\n     * 转换输出控制器文件名称\n     *\n     * @since 3.5.0\n     */\n    private ConverterFileName converterFileName = (entityName -> entityName + ConstVal.CONTROLLER);\n\n    /**\n     * 是否覆盖已有文件（默认 false）\n     *\n     * @since 3.5.2\n     */\n    @Getter\n    private boolean fileOverride;\n\n    /**\n     * 是否生成\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generate = true;\n\n    /**\n     * 模板路径\n     * @since 3.5.6\n     */\n    @Getter\n    private String templatePath = ConstVal.TEMPLATE_CONTROLLER;\n\n    @Nullable\n    public String getSuperClass() {\n        return superClass;\n    }\n\n    @NotNull\n    public ConverterFileName getConverterFileName() {\n        return converterFileName;\n    }\n\n    @Override\n    @NotNull\n    public Map<String, Object> renderData(@NotNull TableInfo tableInfo) {\n        Map<String, Object> data = new HashMap<>(5);\n        data.put(\"controllerMappingHyphen\", StringUtils.camelToHyphen(tableInfo.getEntityPath()));\n        data.put(\"controllerMappingHyphenStyle\", this.hyphenStyle);\n        data.put(\"restControllerStyle\", this.restStyle);\n        data.put(\"superControllerClassPackage\", StringUtils.isBlank(superClass) ? null : superClass);\n        data.put(\"superControllerClass\", ClassUtils.getSimpleName(this.superClass));\n        return data;\n    }\n\n    public static class Builder extends BaseBuilder {\n\n        private final Controller controller = new Controller();\n\n        public Builder(@NotNull StrategyConfig strategyConfig) {\n            super(strategyConfig);\n        }\n\n        /**\n         * 父类控制器\n         *\n         * @param clazz 父类控制器\n         * @return this\n         */\n        public Builder superClass(@NotNull Class<?> clazz) {\n            return superClass(clazz.getName());\n        }\n\n        /**\n         * 父类控制器\n         *\n         * @param superClass 父类控制器类名\n         * @return this\n         */\n        public Builder superClass(@NotNull String superClass) {\n            this.controller.superClass = superClass;\n            return this;\n        }\n\n        /**\n         * 开启驼峰转连字符\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableHyphenStyle() {\n            this.controller.hyphenStyle = true;\n            return this;\n        }\n\n        /**\n         * 开启生成@RestController控制器\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableRestStyle() {\n            this.controller.restStyle = true;\n            return this;\n        }\n\n        /**\n         * 转换输出文件名称\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertFileName(@NotNull ConverterFileName converter) {\n            this.controller.converterFileName = converter;\n            return this;\n        }\n\n        /**\n         * 格式化文件名称\n         *\n         * @param format 　格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatFileName(@NotNull String format) {\n            return convertFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 覆盖已有文件（该方法后续会删除，替代方法为enableFileOverride方法）\n         *\n         * @see #enableFileOverride()\n         */\n        @Deprecated\n        public Builder fileOverride() {\n            this.controller.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 覆盖已有文件\n         *\n         * @since 3.5.3\n         */\n        public Builder enableFileOverride() {\n            this.controller.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 禁用生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disable() {\n            this.controller.generate = false;\n            return this;\n        }\n\n        /**\n         * 指定模板路径\n         *\n         * @param template 模板路径\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder template(@NotNull String template) {\n            this.controller.templatePath = template;\n            return this;\n        }\n\n        @NotNull\n        public Controller get() {\n            return this.controller;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/CustomFile.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.config.IConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport lombok.Getter;\n\nimport java.util.function.Function;\n\n/**\n * 自定义模板文件配置\n *\n * @author xusimin\n * @since 3.5.3\n */\n@Getter\npublic class CustomFile {\n\n    /**\n     * 文件名称格式化函数\n     */\n    private Function<TableInfo, String> formatNameFunction;\n\n    /**\n     * 文件名称\n     */\n    private String fileName;\n\n    /**\n     * 模板路径\n     */\n    private String templatePath;\n\n    /**\n     * 自定义文件包名\n     */\n    private String packageName;\n\n    /**\n     * 文件路径\n     */\n    private String filePath;\n\n    /**\n     * 是否覆盖已有文件（默认 false）\n     */\n    private boolean fileOverride;\n\n    /**\n     * 获取文件短名称(去除后缀)\n     *\n     * @return 文件名\n     * @since 3.5.10\n     */\n    public String getShortName() {\n        return fileName.substring(0, fileName.lastIndexOf(\".\"));\n    }\n\n    /**\n     * 构建者\n     */\n    public static class Builder implements IConfigBuilder<CustomFile> {\n\n        private final CustomFile customFile;\n\n        public Builder() {\n            this.customFile = new CustomFile();\n        }\n\n        /**\n         * 文件名称格式化函数\n         */\n        public CustomFile.Builder formatNameFunction(Function<TableInfo, String> formatNameFunction) {\n            this.customFile.formatNameFunction = formatNameFunction;\n            return this;\n        }\n\n        /**\n         * 文件名称\n         */\n        public CustomFile.Builder fileName(String fileName) {\n            this.customFile.fileName = fileName;\n            return this;\n        }\n\n        /**\n         * 模板路径\n         */\n        public CustomFile.Builder templatePath(String templatePath) {\n            this.customFile.templatePath = templatePath;\n            return this;\n        }\n\n        /**\n         * 包路径\n         */\n        public CustomFile.Builder packageName(String packageName) {\n            this.customFile.packageName = packageName;\n            return this;\n        }\n\n        /**\n         * 文件路径，默认为 PackageConfig.parent 路径\n         */\n        public CustomFile.Builder filePath(String filePath) {\n            this.customFile.filePath = filePath;\n            return this;\n        }\n\n        /**\n         * 覆盖已有文件\n         */\n        public CustomFile.Builder enableFileOverride() {\n            this.customFile.fileOverride = true;\n            return this;\n        }\n\n        @Override\n        public CustomFile build() {\n            return this.customFile;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/Entity.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.DefaultTableAnnotationHandler;\nimport com.baomidou.mybatisplus.generator.DefaultTableFieldAnnotationHandler;\nimport com.baomidou.mybatisplus.generator.IFill;\nimport com.baomidou.mybatisplus.generator.ITableAnnotationHandler;\nimport com.baomidou.mybatisplus.generator.ITableFieldAnnotationHandler;\nimport com.baomidou.mybatisplus.generator.ITemplate;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.INameConvert;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport com.baomidou.mybatisplus.generator.function.ConverterFileName;\nimport com.baomidou.mybatisplus.generator.model.AnnotationAttributes;\nimport com.baomidou.mybatisplus.generator.model.ClassAnnotationAttributes;\nimport com.baomidou.mybatisplus.generator.util.ClassUtils;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 实体属性配置\n *\n * @author nieqiurong 2020/10/11.\n * @since 3.5.0\n */\npublic class Entity implements ITemplate {\n\n    private final AnnotationHandler annotationHandler = new AnnotationHandler() {\n    };\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(Entity.class);\n\n    /**\n     * Java模板默认路径\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private String javaTemplate = ConstVal.TEMPLATE_ENTITY_JAVA;\n\n    /**\n     * Kotlin模板默认路径\n     */\n    @Getter\n    private String kotlinTemplate = ConstVal.TEMPLATE_ENTITY_KT;\n\n    private Entity() {\n    }\n\n    /**\n     * 名称转换\n     */\n    private INameConvert nameConvert;\n\n    /**\n     * 自定义继承的Entity类全称，带包名\n     */\n    private String superClass;\n\n    /**\n     * 自定义基础的Entity类，公共字段\n     */\n    @Getter\n    private final Set<String> superEntityColumns = new HashSet<>();\n\n    /**\n     * 自定义忽略字段\n     * <a href=\"https://github.com/baomidou/generator/issues/46\">...</a>\n     */\n    private final Set<String> ignoreColumns = new HashSet<>();\n\n    /**\n     * 实体是否生成 serialVersionUID\n     */\n    @Getter\n    private boolean serialVersionUID = true;\n\n    /**\n     * 是否启用 {@link java.io.Serial} (需JAVA 14) 注解\n     *\n     * @since 3.5.11\n     */\n    @Getter\n    private boolean serialAnnotation;\n\n    /**\n     * 【实体】是否生成字段常量（默认 false）<br>\n     * -----------------------------------<br>\n     * public static final String ID = \"test_id\";\n     */\n    @Getter\n    private boolean columnConstant;\n\n    /**\n     * 【实体】是否为链式模型（默认 false）\n     *\n     * @since 3.3.2\n     */\n    @Getter\n    private boolean chain;\n\n    /**\n     * 【实体】是否为lombok模型（默认 false）<br>\n     * <a href=\"https://projectlombok.org/\">document</a>\n     */\n    @Getter\n    private boolean lombok;\n\n    /**\n     * Boolean类型字段是否移除is前缀（默认 false）<br>\n     * 比如 : 数据库字段名称 : 'is_xxx',类型为 : tinyint. 在映射实体的时候则会去掉is,在实体类中映射最终结果为 xxx\n     */\n    @Getter\n    private boolean booleanColumnRemoveIsPrefix;\n\n    /**\n     * 是否生成实体时，生成字段注解（默认 false）\n     */\n    @Getter\n    private boolean tableFieldAnnotationEnable;\n\n    /**\n     * 乐观锁字段名称(数据库字段)\n     *\n     * @since 3.5.0\n     */\n    private String versionColumnName;\n\n    /**\n     * 乐观锁属性名称(实体字段)\n     *\n     * @since 3.5.0\n     */\n    private String versionPropertyName;\n\n    /**\n     * 逻辑删除字段名称(数据库字段)\n     *\n     * @since 3.5.0\n     */\n    private String logicDeleteColumnName;\n\n    /**\n     * 逻辑删除属性名称(实体字段)\n     *\n     * @since 3.5.0\n     */\n    private String logicDeletePropertyName;\n\n    /**\n     * 表填充字段\n     */\n    private final List<IFill> tableFillList = new ArrayList<>();\n\n    /**\n     * 数据库表映射到实体的命名策略，默认下划线转驼峰命名\n     */\n    private NamingStrategy naming = NamingStrategy.underline_to_camel;\n\n    /**\n     * 数据库表字段映射到实体的命名策略\n     * <p>未指定按照 naming 执行</p>\n     */\n    private NamingStrategy columnNaming = null;\n\n    /**\n     * 开启 ActiveRecord 模式（默认 false）\n     *\n     * @since 3.5.0\n     */\n    @Getter\n    private boolean activeRecord;\n\n    /**\n     * 指定生成的主键的ID类型\n     *\n     * @since 3.5.0\n     */\n    private IdType idType;\n\n    /**\n     * 转换输出文件名称\n     *\n     * @since 3.5.0\n     */\n    private ConverterFileName converterFileName = (entityName -> entityName);\n\n    /**\n     * 是否覆盖已有文件（默认 false）\n     *\n     * @since 3.5.2\n     */\n    @Getter\n    private boolean fileOverride;\n\n\n    /**\n     * 是否生成\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generate = true;\n\n    /**\n     * 默认lombok(低版本属性默认只有Getter和Setter)\n     * <p>当升级至3.5.10后,默认启用@ToString,如果不需要,可通过{@link Builder#toString(boolean)}关闭</p>\n     *\n     * @since 3.5.10\n     */\n    @Getter\n    private boolean defaultLombok = true;\n\n    /**\n     * 是否生成ToString\n     * <p>低版本下,lombok没有处理ToString逻辑,现在处理生成@ToString</p>\n     * <p>支持控制toString方法是否生成</p>\n     *\n     * @since 3.5.10\n     */\n    @Getter\n    private boolean toString = true;\n\n\n    /**\n     * 启用字段文档注释 (当注释字段注释不为空才生效)\n     * <p>低版本下,如果是启用swagger或者springdoc时,不会生成,现在统一修改为生成文档注释</p>\n     *\n     * @since 3.5.10\n     */\n    @Getter\n    private boolean fieldUseJavaDoc = true;\n\n    /**\n     * 实体类注解\n     *\n     * @since 3.5.10\n     */\n    @Getter\n    private final List<ClassAnnotationAttributes> classAnnotations = new ArrayList<>();\n\n    /**\n     * 表注解处理器\n     *\n     * @since 3.5.10\n     */\n    private ITableAnnotationHandler tableAnnotationHandler = new DefaultTableAnnotationHandler();\n\n    /**\n     * 字段注解处理器\n     *\n     * @since 3.5.10\n     */\n    private ITableFieldAnnotationHandler tableFieldAnnotationHandler = new DefaultTableFieldAnnotationHandler();\n\n    /**\n     * 导包处理方法\n     *\n     * @since 3.5.11\n     */\n    private Function<Set<String>, List<String>> importPackageFunction;\n\n    /**\n     * 处理类注解方法 (含类与字段)\n     *\n     * @since 3.5.11\n     */\n    private Function<List<? extends AnnotationAttributes>, List<AnnotationAttributes>> annotationAttributesFunction;\n\n\n    /**\n     * <p>\n     * 父类 Class 反射属性转换为公共字段\n     * </p>\n     *\n     * @param clazz 实体父类 Class\n     */\n    public void convertSuperEntityColumns(Class<?> clazz) {\n        List<Field> fields = TableInfoHelper.getAllFields(clazz, annotationHandler);\n        this.superEntityColumns.addAll(fields.stream().map(field -> {\n            TableId tableId = annotationHandler.getAnnotation(field, TableId.class);\n            if (tableId != null && StringUtils.isNotBlank(tableId.value())) {\n                return tableId.value();\n            }\n            TableField tableField = annotationHandler.getAnnotation(field, TableField.class);\n            if (tableField != null && StringUtils.isNotBlank(tableField.value())) {\n                return tableField.value();\n            }\n            if (null == columnNaming || columnNaming == NamingStrategy.no_change) {\n                return field.getName();\n            }\n            return StringUtils.camelToUnderline(field.getName());\n        }).collect(Collectors.toSet()));\n    }\n\n    @NotNull\n    public NamingStrategy getColumnNaming() {\n        // 未指定以 naming 策略为准\n        return Optional.ofNullable(columnNaming).orElse(naming);\n    }\n\n    /**\n     * 匹配父类字段(忽略大小写)\n     *\n     * @param fieldName 字段名\n     * @return 是否匹配\n     * @since 3.5.0\n     */\n    public boolean matchSuperEntityColumns(String fieldName) {\n        // 公共字段判断忽略大小写【 部分数据库大小写不敏感 】\n        return superEntityColumns.stream().anyMatch(e -> e.equalsIgnoreCase(fieldName));\n    }\n\n    /**\n     * 匹配忽略字段(忽略大小写)\n     *\n     * @param fieldName 字段名\n     * @return 是否匹配\n     * @since 3.5.0\n     */\n    public boolean matchIgnoreColumns(String fieldName) {\n        return ignoreColumns.stream().anyMatch(e -> e.equalsIgnoreCase(fieldName));\n    }\n\n    @NotNull\n    public INameConvert getNameConvert() {\n        return nameConvert;\n    }\n\n    @Nullable\n    public String getSuperClass() {\n        return superClass;\n    }\n\n    @Nullable\n    public String getVersionColumnName() {\n        return versionColumnName;\n    }\n\n    @Nullable\n    public String getVersionPropertyName() {\n        return versionPropertyName;\n    }\n\n    @Nullable\n    public String getLogicDeleteColumnName() {\n        return logicDeleteColumnName;\n    }\n\n    @Nullable\n    public String getLogicDeletePropertyName() {\n        return logicDeletePropertyName;\n    }\n\n    @NotNull\n    public List<IFill> getTableFillList() {\n        return tableFillList;\n    }\n\n    @NotNull\n    public NamingStrategy getNaming() {\n        return naming;\n    }\n\n    @Nullable\n    public IdType getIdType() {\n        return idType;\n    }\n\n    @NotNull\n    public ConverterFileName getConverterFileName() {\n        return converterFileName;\n    }\n\n    @Override\n    @NotNull\n    public Map<String, Object> renderData(@NotNull TableInfo tableInfo) {\n        Map<String, Object> data = new HashMap<>();\n        data.put(\"idType\", idType == null ? null : idType.toString());\n        data.put(\"logicDeleteFieldName\", this.logicDeleteColumnName);\n        data.put(\"versionFieldName\", this.versionColumnName);\n        data.put(\"activeRecord\", this.activeRecord);\n        data.put(\"entitySerialVersionUID\", this.serialVersionUID);\n        data.put(\"entitySerialAnnotation\", this.serialAnnotation);\n        data.put(\"entityColumnConstant\", this.columnConstant);\n        data.put(\"entityBuilderModel\", this.chain);\n        data.put(\"chainModel\", this.chain);\n        data.put(\"entityLombokModel\", this.lombok);\n        data.put(\"entityBooleanColumnRemoveIsPrefix\", this.booleanColumnRemoveIsPrefix);\n        data.put(\"superEntityClass\", ClassUtils.getSimpleName(this.superClass));\n        Set<String> importPackages = new HashSet<>(tableInfo.getImportPackages());\n        List<ClassAnnotationAttributes> classAnnotationAttributes = new ArrayList<>(this.getClassAnnotations());\n        if (tableAnnotationHandler != null) {\n            List<ClassAnnotationAttributes> classAnnotationAttributesList = tableAnnotationHandler.handle(tableInfo, this);\n            if (classAnnotationAttributesList != null && !classAnnotationAttributesList.isEmpty()) {\n                classAnnotationAttributes.addAll(classAnnotationAttributesList);\n            }\n        }\n        classAnnotationAttributes.forEach(attributes -> {\n            attributes.handleDisplayName(tableInfo);\n            importPackages.addAll(attributes.getImportPackages());\n        });\n        if (tableFieldAnnotationHandler != null) {\n            tableInfo.getFields().forEach(tableField -> {\n                List<AnnotationAttributes> annotationAttributes = tableFieldAnnotationHandler.handle(tableInfo, tableField);\n                if (annotationAttributes != null && !annotationAttributes.isEmpty()) {\n                    tableField.addAnnotationAttributesList(annotationAttributes, annotationAttributesFunction);\n                    annotationAttributes.forEach(attributes -> importPackages.addAll(attributes.getImportPackages()));\n                }\n            });\n        }\n        data.put(\"entityFieldUseJavaDoc\", fieldUseJavaDoc);\n        data.put(\"entityClassAnnotations\", annotationAttributesFunction != null ? annotationAttributesFunction.apply(classAnnotationAttributes) :\n            classAnnotationAttributes.stream().sorted(Comparator.comparingInt(s -> s.getDisplayName().length())).collect(Collectors.toList()));\n        data.put(\"importEntityPackages\", importPackageFunction != null ? importPackageFunction.apply(importPackages) :\n            importPackages.stream().sorted().collect(Collectors.toList()));\n        Set<String> javaPackages = importPackages.stream().filter(pkg -> pkg.startsWith(\"java\")).collect(Collectors.toSet());\n        data.put(\"importEntityJavaPackages\", importPackageFunction != null ? importPackageFunction.apply(javaPackages) :\n            javaPackages.stream().sorted().collect(Collectors.toList()));\n        Set<String> frameworkPackages = importPackages.stream().filter(pkg -> !pkg.startsWith(\"java\")).collect(Collectors.toSet());\n        data.put(\"importEntityFrameworkPackages\", importPackageFunction != null ? importPackageFunction.apply(frameworkPackages) :\n            frameworkPackages.stream().sorted().collect(Collectors.toList()));\n        data.put(\"entityToString\", this.toString);\n        return data;\n    }\n\n    public static class Builder extends BaseBuilder {\n\n        private final Entity entity = new Entity();\n\n        public Builder(StrategyConfig strategyConfig) {\n            super(strategyConfig);\n            this.entity.nameConvert = new INameConvert.DefaultNameConvert(strategyConfig);\n        }\n\n        /**\n         * 名称转换实现\n         *\n         * @param nameConvert 名称转换实现\n         * @return this\n         */\n        public Builder nameConvert(INameConvert nameConvert) {\n            this.entity.nameConvert = nameConvert;\n            return this;\n        }\n\n        /**\n         * 自定义继承的Entity类全称\n         *\n         * @param clazz 类\n         * @return this\n         */\n        public Builder superClass(@NotNull Class<?> clazz) {\n            return superClass(clazz.getName());\n        }\n\n        /**\n         * 自定义继承的Entity类全称，带包名\n         *\n         * @param superEntityClass 类全称\n         * @return this\n         */\n        public Builder superClass(String superEntityClass) {\n            this.entity.superClass = superEntityClass;\n            return this;\n        }\n\n        /**\n         * 禁用生成serialVersionUID\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder disableSerialVersionUID() {\n            this.entity.serialVersionUID = false;\n            return this;\n        }\n\n        /**\n         * 启用生成 {@link java.io.Serial} (需JAVA 14)\n         * <p>当开启了 {@link #serialVersionUID} 时,会增加 {@link java.io.Serial} 注解在此字段上</p>\n         *\n         * @return this\n         * @since 3.5.11\n         */\n        public Builder enableSerialAnnotation() {\n            this.entity.serialAnnotation = true;\n            return this;\n        }\n\n        /**\n         * 开启生成字段常量\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableColumnConstant() {\n            this.entity.columnConstant = true;\n            return this;\n        }\n\n        /**\n         * 开启链式模型\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableChainModel() {\n            this.entity.chain = true;\n            return this;\n        }\n\n        /**\n         * 开启lombok模型 (默认添加Getter和Setter)\n         * <p>自3.5.10开始,默认添加ToString搭配,如果想关闭可通过{@link #toString(boolean)}关闭</p>\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableLombok() {\n            this.entity.lombok = true;\n            return this;\n        }\n\n        /**\n         * 开启lombok模型 (会把注解属性都加入进去,无论是否启用{@link GlobalConfig#isKotlin()})\n         * <p>注意如果通过此方法开启lombok模型,默认的lombok注解(get,set,toString)都将不会生成,请自行控制添加</p>\n         * <p>由{@link #toString(boolean)}控制的也会失效</p>\n         * 使用@Data示例: enableLombok(new ClassAnnotationAttributes(\"@Data\",\"lombok.Data\"))\n         *\n         * @param attributes 注解属性集合\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder enableLombok(@NotNull ClassAnnotationAttributes... attributes) {\n            this.entity.lombok = true;\n            this.entity.defaultLombok = false;\n            for (ClassAnnotationAttributes attribute : attributes) {\n                this.addClassAnnotation(attribute);\n            }\n            return this;\n        }\n\n        /**\n         * 开启Boolean类型字段移除is前缀\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableRemoveIsPrefix() {\n            this.entity.booleanColumnRemoveIsPrefix = true;\n            return this;\n        }\n\n        /**\n         * 开启生成实体时生成字段注解\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableTableFieldAnnotation() {\n            this.entity.tableFieldAnnotationEnable = true;\n            return this;\n        }\n\n        /**\n         * 开启 ActiveRecord 模式\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableActiveRecord() {\n            this.entity.activeRecord = true;\n            return this;\n        }\n\n        /**\n         * 设置乐观锁数据库表字段名称\n         *\n         * @param versionColumnName 乐观锁数据库字段名称\n         * @return this\n         */\n        public Builder versionColumnName(String versionColumnName) {\n            this.entity.versionColumnName = versionColumnName;\n            return this;\n        }\n\n        /**\n         * 设置乐观锁实体属性字段名称\n         *\n         * @param versionPropertyName 乐观锁实体属性字段名称\n         * @return this\n         */\n        public Builder versionPropertyName(String versionPropertyName) {\n            this.entity.versionPropertyName = versionPropertyName;\n            return this;\n        }\n\n        /**\n         * 逻辑删除数据库字段名称\n         *\n         * @param logicDeleteColumnName 逻辑删除字段名称\n         * @return this\n         */\n        public Builder logicDeleteColumnName(String logicDeleteColumnName) {\n            this.entity.logicDeleteColumnName = logicDeleteColumnName;\n            return this;\n        }\n\n        /**\n         * 逻辑删除实体属性名称\n         *\n         * @param logicDeletePropertyName 逻辑删除实体属性名称\n         * @return this\n         */\n        public Builder logicDeletePropertyName(String logicDeletePropertyName) {\n            this.entity.logicDeletePropertyName = logicDeletePropertyName;\n            return this;\n        }\n\n        /**\n         * 数据库表映射到实体的命名策略\n         *\n         * @param namingStrategy 数据库表映射到实体的命名策略\n         * @return this\n         */\n        public Builder naming(NamingStrategy namingStrategy) {\n            this.entity.naming = namingStrategy;\n            return this;\n        }\n\n        /**\n         * 数据库表字段映射到实体的命名策略\n         *\n         * @param namingStrategy 数据库表字段映射到实体的命名策略\n         * @return this\n         */\n        public Builder columnNaming(NamingStrategy namingStrategy) {\n            this.entity.columnNaming = namingStrategy;\n            return this;\n        }\n\n        /**\n         * 添加父类公共字段\n         *\n         * @param superEntityColumns 父类字段(数据库字段列名)\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addSuperEntityColumns(@NotNull String... superEntityColumns) {\n            return addSuperEntityColumns(Arrays.asList(superEntityColumns));\n        }\n\n        public Builder addSuperEntityColumns(@NotNull List<String> superEntityColumnList) {\n            this.entity.superEntityColumns.addAll(superEntityColumnList);\n            return this;\n        }\n\n        /**\n         * 添加忽略字段\n         *\n         * @param ignoreColumns 需要忽略的字段(数据库字段列名)\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addIgnoreColumns(@NotNull String... ignoreColumns) {\n            return addIgnoreColumns(Arrays.asList(ignoreColumns));\n        }\n\n        public Builder addIgnoreColumns(@NotNull List<String> ignoreColumnList) {\n            this.entity.ignoreColumns.addAll(ignoreColumnList);\n            return this;\n        }\n\n        /**\n         * 添加表字段填充\n         *\n         * @param tableFills 填充字段\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addTableFills(@NotNull IFill... tableFills) {\n            return addTableFills(Arrays.asList(tableFills));\n        }\n\n        /**\n         * 添加表字段填充\n         *\n         * @param tableFillList 填充字段集合\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder addTableFills(@NotNull List<IFill> tableFillList) {\n            this.entity.tableFillList.addAll(tableFillList);\n            return this;\n        }\n\n        /**\n         * 指定生成的主键的ID类型\n         *\n         * @param idType ID类型\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder idType(IdType idType) {\n            this.entity.idType = idType;\n            return this;\n        }\n\n        /**\n         * 转换输出文件名称\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertFileName(@NotNull ConverterFileName converter) {\n            this.entity.converterFileName = converter;\n            return this;\n        }\n\n        /**\n         * 格式化文件名称\n         *\n         * @param format 　格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatFileName(String format) {\n            return convertFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 覆盖已有文件（该方法后续会删除，替代方法为enableFileOverride方法）\n         *\n         * @see #enableFileOverride()\n         */\n        @Deprecated\n        public Builder fileOverride() {\n            LOGGER.warn(\"fileOverride方法后续会删除，替代方法为enableFileOverride方法\");\n            this.entity.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 覆盖已有文件\n         *\n         * @since 3.5.3\n         */\n        public Builder enableFileOverride() {\n            this.entity.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 指定模板路径\n         *\n         * @param template 模板路径\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder javaTemplate(String template) {\n            this.entity.javaTemplate = template;\n            return this;\n        }\n\n        /**\n         * 指定模板路径\n         *\n         * @param template 模板路径\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder kotlinTemplatePath(String template) {\n            this.entity.kotlinTemplate = template;\n            return this;\n        }\n\n        /**\n         * 禁用实体生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disable() {\n            this.entity.generate = false;\n            return this;\n        }\n\n        /**\n         * 添加类注解\n         *\n         * @param attributes 注解属性\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder addClassAnnotation(@NotNull ClassAnnotationAttributes attributes) {\n            this.entity.classAnnotations.add(attributes);\n            return this;\n        }\n\n        /**\n         * 指定字段注解处理器\n         *\n         * @param tableFieldAnnotationHandler 字段处理器\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder tableFieldAnnotationHandler(@NotNull ITableFieldAnnotationHandler tableFieldAnnotationHandler) {\n            this.entity.tableFieldAnnotationHandler = tableFieldAnnotationHandler;\n            return this;\n        }\n\n        /**\n         * 指定表注解处理器\n         * @param tableAnnotationHandler 表注解处理器\n         * @since 3.5.10\n         * @return this\n         */\n        public Builder tableAnnotationHandler(@NotNull ITableAnnotationHandler tableAnnotationHandler){\n            this.entity.tableAnnotationHandler = tableAnnotationHandler;\n            return this;\n        }\n\n        /**\n         * 设置是否生成ToString方法\n         *\n         * @param toString 是否生成\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder toString(boolean toString) {\n            this.entity.toString = toString;\n            return this;\n        }\n\n        /**\n         * 设置字段是否生成文档注释\n         *\n         * @param fieldUseJavaDoc 是否生成文档注释\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder fieldUseJavaDoc(boolean fieldUseJavaDoc) {\n            this.entity.fieldUseJavaDoc = fieldUseJavaDoc;\n            return this;\n        }\n\n        /**\n         * 导包处理方法\n         *\n         * @param importPackageFunction 导包处理\n         * @return this\n         * @since 3.5.11\n         */\n        public Builder importPackageFunction(Function<Set<String>, List<String>> importPackageFunction) {\n            this.entity.importPackageFunction = importPackageFunction;\n            return this;\n        }\n\n        /**\n         * 注解处理方法 (含类与字段)\n         *\n         * @param annotationAttributesFunction 注解处理\n         * @return this\n         * @since 3.5.11\n         */\n        public Builder annotationAttributesFunction(Function<List<? extends AnnotationAttributes>, List<AnnotationAttributes>> annotationAttributesFunction) {\n            this.entity.annotationAttributesFunction = annotationAttributesFunction;\n            return this;\n        }\n\n        public Entity get() {\n            String superClass = this.entity.superClass;\n            if (StringUtils.isNotBlank(superClass)) {\n                tryLoadClass(superClass).ifPresent(this.entity::convertSuperEntityColumns);\n            } else {\n                if (!this.entity.superEntityColumns.isEmpty()) {\n                    LOGGER.warn(\"Forgot to set entity supper class ?\");\n                }\n            }\n            return this.entity;\n        }\n\n        private Optional<Class<?>> tryLoadClass(String className) {\n            try {\n                return Optional.of(ClassUtils.toClassConfident(className));\n            } catch (Exception e) {\n                //当父类实体存在类加载器的时候,识别父类实体字段，不存在的情况就只有通过指定superEntityColumns属性了。\n            }\n            return Optional.empty();\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/GeneratorBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.config.*;\n\n/**\n * 生成器 Builder\n *\n * @author hubin 2021/02/08\n * @since 3.5.0\n */\npublic class GeneratorBuilder {\n\n    /**\n     * 全局配置\n     *\n     * @return GlobalConfig\n     */\n    public static GlobalConfig globalConfig() {\n        return new GlobalConfig.Builder().build();\n    }\n\n    /**\n     * 全局配置 Builder\n     *\n     * @return GlobalConfig.Builder\n     */\n    public static GlobalConfig.Builder globalConfigBuilder() {\n        return new GlobalConfig.Builder();\n    }\n\n    /**\n     * 包相关的配置项\n     *\n     * @return PackageConfig\n     */\n    public static PackageConfig packageConfig() {\n        return new PackageConfig.Builder().build();\n    }\n\n    /**\n     * 包相关的配置项 Builder\n     *\n     * @return PackageConfig.Builder\n     */\n    public static PackageConfig.Builder packageConfigBuilder() {\n        return new PackageConfig.Builder();\n    }\n\n    /**\n     * 策略配置项\n     *\n     * @return StrategyConfig\n     */\n    public static StrategyConfig strategyConfig() {\n        return new StrategyConfig.Builder().build();\n    }\n\n    /**\n     * 策略配置项 Builder\n     *\n     * @return StrategyConfig.Builder\n     */\n    public static StrategyConfig.Builder strategyConfigBuilder() {\n        return new StrategyConfig.Builder();\n    }\n\n    /**\n     * 模板路径配置项\n     *\n     * @return TemplateConfig\n     * @deprecated 3.5.6 {@link #strategyConfig()}\n     */\n    @Deprecated\n    public static TemplateConfig templateConfig() {\n        return new TemplateConfig.Builder().build();\n    }\n\n    /**\n     * 模板路径配置项 Builder\n     *\n     * @return TemplateConfig.Builder\n     * @deprecated 3.5.6 {@link #strategyConfigBuilder()}\n     */\n    @Deprecated\n    public static TemplateConfig.Builder templateConfigBuilder() {\n        return new TemplateConfig.Builder();\n    }\n\n    /**\n     * 注入配置项\n     *\n     * @return InjectionConfig\n     */\n    public static InjectionConfig injectionConfig() {\n        return new InjectionConfig.Builder().build();\n    }\n\n    /**\n     * 注入配置项 Builder\n     *\n     * @return InjectionConfig.Builder\n     */\n    public static InjectionConfig.Builder injectionConfigBuilder() {\n        return new InjectionConfig.Builder();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/Mapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.ITemplate;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.PackageConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.function.ConverterFileName;\nimport com.baomidou.mybatisplus.generator.IGenerateMapperMethodHandler;\nimport com.baomidou.mybatisplus.generator.model.MapperMethod;\nimport com.baomidou.mybatisplus.generator.util.ClassUtils;\nimport lombok.Getter;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.cache.decorators.LoggingCache;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.lang.annotation.Annotation;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 控制器属性配置\n *\n * @author nieqiurong 2020/10/11.\n * @since 3.5.0\n */\npublic class Mapper implements ITemplate {\n\n    private Mapper() {\n    }\n\n    /**\n     * 自定义继承的Mapper类全称，带包名\n     */\n    private String superClass = ConstVal.SUPER_MAPPER_CLASS;\n\n    /**\n     * 是否添加 @Mapper 注解（默认 false）\n     *\n     * @see #mapperAnnotationClass\n     * @since 3.5.1\n     * @deprecated 3.5.4\n     */\n    @Deprecated\n    private boolean mapperAnnotation;\n\n    /**\n     * Mapper标记注解\n     *\n     * @since 3.5.3\n     */\n    private Class<? extends Annotation> mapperAnnotationClass;\n\n    /**\n     * 是否开启BaseResultMap（默认 false）\n     *\n     * @since 3.5.0\n     */\n    @Getter\n    private boolean baseResultMap;\n\n    /**\n     * 是否开启baseColumnList（默认 false）\n     *\n     * @since 3.5.0\n     */\n    @Getter\n    private boolean baseColumnList;\n\n    /**\n     * 转换输出Mapper文件名称\n     *\n     * @since 3.5.0\n     */\n    @Getter\n    private ConverterFileName converterMapperFileName = (entityName -> entityName + ConstVal.MAPPER);\n\n    /**\n     * 转换输出Xml文件名称\n     *\n     * @since 3.5.0\n     */\n    @Getter\n    private ConverterFileName converterXmlFileName = (entityName -> entityName + ConstVal.MAPPER);\n\n    /**\n     * 是否覆盖已有文件（默认 false）\n     *\n     * @since 3.5.2\n     */\n    @Getter\n    private boolean fileOverride;\n\n    /**\n     * 设置缓存实现类\n     *\n     * @since 3.5.0\n     */\n    private Class<? extends Cache> cache;\n\n    /**\n     * 是否生成XML\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generateMapperXml = true;\n\n    /**\n     * 是否生成Mapper\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generateMapper = true;\n\n    /**\n     * Mapper模板路径\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private String mapperTemplatePath = ConstVal.TEMPLATE_MAPPER;\n\n    /**\n     * MapperXml模板路径\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private String mapperXmlTemplatePath = ConstVal.TEMPLATE_XML;\n\n    @NotNull\n    public String getSuperClass() {\n        return superClass;\n    }\n\n    @Deprecated\n    public boolean isMapperAnnotation() {\n        return mapperAnnotationClass != null;\n    }\n\n    public Class<? extends Cache> getCache() {\n        return this.cache == null ? LoggingCache.class : this.cache;\n    }\n\n    /**\n     * Mapper层方法生成\n     *\n     * @since 3.5.10\n     */\n    private IGenerateMapperMethodHandler generateMapperMethodHandler;\n\n    /**\n     * 导包处理方法\n     *\n     * @since 3.5.11\n     */\n    private Function<Set<String>, List<String>> importPackageFunction;\n\n\n    @Override\n    @NotNull\n    public Map<String, Object> renderData(@NotNull TableInfo tableInfo) {\n        Map<String, Object> data = new HashMap<>();\n        boolean enableCache = this.cache != null;\n        data.put(\"enableCache\", enableCache);\n        data.put(\"mapperAnnotation\", mapperAnnotationClass != null);\n        data.put(\"mapperAnnotationClass\", mapperAnnotationClass);\n        data.put(\"baseResultMap\", this.baseResultMap);\n        data.put(\"baseColumnList\", this.baseColumnList);\n        data.put(\"superMapperClassPackage\", this.superClass);\n        if (enableCache) {\n            Class<? extends Cache> cacheClass = this.getCache();\n            data.put(\"cache\", cacheClass);\n            data.put(\"cacheClassName\", cacheClass.getName());\n        }\n        data.put(\"superMapperClass\", ClassUtils.getSimpleName(this.superClass));\n        data.put(\"generateMapperXml\", this.generateMapperXml);\n        data.put(\"generateMapper\", this.generateMapper);\n        List<MapperMethod> methodList = null;\n        Set<String> importPackages = new HashSet<>();\n        if (generateMapperMethodHandler != null) {\n            methodList = generateMapperMethodHandler.getMethodList(tableInfo);\n            importPackages.addAll(generateMapperMethodHandler.getImportPackages(tableInfo));\n        }\n        if(StringUtils.isNotBlank(superClass)){\n            importPackages.add(superClass);\n        }\n        if (mapperAnnotationClass != null) {\n            importPackages.add(mapperAnnotationClass.getName());\n        }\n        PackageConfig packageConfig = tableInfo.getPackageConfig();\n        String entityPackage = packageConfig.getPackageInfo(null, ConstVal.ENTITY) + StringPool.DOT + tableInfo.getEntityName();\n        importPackages.add(entityPackage);\n        Set<String> javaPackages = importPackages.stream().filter(pkg -> pkg.startsWith(\"java\")).collect(Collectors.toSet());\n        Set<String> frameworkPackages = importPackages.stream().filter(pkg -> !pkg.startsWith(\"java\")).collect(Collectors.toSet());\n        data.put(\"importPackages\", importPackageFunction != null ? importPackageFunction.apply(importPackages) : importPackages.stream().sorted().collect(Collectors.toList()));\n        data.put(\"importMapperFrameworkPackages\", importPackageFunction != null ? importPackageFunction.apply(frameworkPackages) : frameworkPackages.stream().sorted().collect(Collectors.toList()));\n        data.put(\"importMapperJavaPackages\", importPackageFunction != null ? importPackageFunction.apply(javaPackages) : javaPackages.stream().sorted().collect(Collectors.toList()));\n        data.put(\"mapperMethodList\", methodList == null ? Collections.emptyList() : methodList);\n        return data;\n    }\n\n    public static class Builder extends BaseBuilder {\n\n        private final Mapper mapper = new Mapper();\n\n        public Builder(StrategyConfig strategyConfig) {\n            super(strategyConfig);\n        }\n\n        /**\n         * 父类Mapper\n         *\n         * @param superClass 类名\n         * @return this\n         */\n        public Builder superClass(@NotNull String superClass) {\n            this.mapper.superClass = superClass;\n            return this;\n        }\n\n        /**\n         * 父类Mapper\n         *\n         * @param superClass 类\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder superClass(@NotNull Class<?> superClass) {\n            return superClass(superClass.getName());\n        }\n\n        /**\n         * 开启 @Mapper 注解\n         *\n         * @return this\n         * @see #mapperAnnotation(Class)\n         * @since 3.5.1\n         * @deprecated 3.5.4\n         */\n        @Deprecated\n        public Builder enableMapperAnnotation() {\n            this.mapper.mapperAnnotation = true;\n            //TODO 因为现在mybatis-plus传递mybatis-spring依赖，这里是没问题的，但后面如果考虑脱离mybatis-spring的时候就需要把这里处理掉，建议使用mapperAnnotation方法来标记自己的注解。\n            this.mapper.mapperAnnotationClass = org.apache.ibatis.annotations.Mapper.class;\n            return this;\n        }\n\n        /**\n         * 标记 Mapper 注解\n         *\n         * @param annotationClass 注解Class\n         * @return this\n         * @since 3.5.3\n         */\n        public Builder mapperAnnotation(Class<? extends Annotation> annotationClass) {\n            this.mapper.mapperAnnotationClass = annotationClass;\n            return this;\n        }\n\n        /**\n         * 开启baseResultMap\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableBaseResultMap() {\n            this.mapper.baseResultMap = true;\n            return this;\n        }\n\n        /**\n         * 开启baseColumnList\n         *\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder enableBaseColumnList() {\n            this.mapper.baseColumnList = true;\n            return this;\n        }\n\n        /**\n         * 设置缓存实现类\n         *\n         * @param cache 缓存实现\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder cache(@NotNull Class<? extends Cache> cache) {\n            this.mapper.cache = cache;\n            return this;\n        }\n\n        /**\n         * 输出Mapper文件名称转换\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertMapperFileName(@NotNull ConverterFileName converter) {\n            this.mapper.converterMapperFileName = converter;\n            return this;\n        }\n\n        /**\n         * 转换Xml文件名称处理\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertXmlFileName(@NotNull ConverterFileName converter) {\n            this.mapper.converterXmlFileName = converter;\n            return this;\n        }\n\n        /**\n         * 格式化Mapper文件名称\n         *\n         * @param format 　格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatMapperFileName(@NotNull String format) {\n            return convertMapperFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 格式化Xml文件名称\n         *\n         * @param format 格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatXmlFileName(@NotNull String format) {\n            return convertXmlFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 覆盖已有文件（该方法后续会删除，替代方法为enableFileOverride方法）\n         *\n         * @see #enableFileOverride()\n         */\n        @Deprecated\n        public Builder fileOverride() {\n            this.mapper.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 覆盖已有文件\n         */\n        public Builder enableFileOverride() {\n            this.mapper.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * Service模板路径\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder mapperTemplate(@NotNull String template) {\n            this.mapper.mapperTemplatePath = template;\n            return this;\n        }\n\n        /**\n         * ServiceImpl模板路径\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder mapperXmlTemplate(@NotNull String template) {\n            this.mapper.mapperXmlTemplatePath = template;\n            return this;\n        }\n\n        /**\n         * 禁用Mapper生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disable() {\n            this.mapper.generateMapper = false;\n            this.mapper.generateMapperXml = false;\n            return this;\n        }\n\n        /**\n         * 禁用Mapper接口生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disableMapper() {\n            this.mapper.generateMapper = false;\n            return this;\n        }\n\n        /**\n         * 禁用MapperXml生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disableMapperXml() {\n            this.mapper.generateMapperXml = false;\n            return this;\n        }\n\n        /**\n         * Mapper层方法生成处理器\n         *\n         * @param generateMapperMethodHandler 处理器\n         * @return this\n         * @since 3.5.10\n         */\n        public Builder generateMapperMethodHandler(IGenerateMapperMethodHandler generateMapperMethodHandler) {\n            this.mapper.generateMapperMethodHandler = generateMapperMethodHandler;\n            return this;\n        }\n\n\n        /**\n         * 导包处理方法\n         *\n         * @param importPackageFunction 导包处理\n         * @return this\n         * @since 3.5.11\n         */\n        public Builder importPackageFunction(Function<Set<String>, List<String>> importPackageFunction) {\n            this.mapper.importPackageFunction = importPackageFunction;\n            return this;\n        }\n\n\n        @NotNull\n        public Mapper get() {\n            return this.mapper;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/PathInfoHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.*;\nimport lombok.Getter;\n\nimport java.io.File;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 路径信息处理\n *\n * @author nieqiurong hubin\n * @since 2020-10-06\n * @since 3.5.0\n */\nclass PathInfoHandler {\n\n    /**\n     * 输出文件Map\n     */\n    @Getter\n    private final Map<OutputFile, String> pathInfo = new HashMap<>();\n\n    /**\n     * 输出目录\n     */\n    private final String outputDir;\n\n    /**\n     * 包配置信息\n     */\n    private final PackageConfig packageConfig;\n\n    PathInfoHandler(InjectionConfig injectionConfig, GlobalConfig globalConfig, StrategyConfig strategyConfig, PackageConfig packageConfig) {\n        this.outputDir = globalConfig.getOutputDir();\n        this.packageConfig = packageConfig;\n        // 设置默认输出路径\n        this.setDefaultPathInfo(injectionConfig, globalConfig, strategyConfig);\n        // 覆盖自定义路径\n        Map<OutputFile, String> pathInfo = packageConfig.getPathInfo();\n        if (CollectionUtils.isNotEmpty(pathInfo)) {\n            this.pathInfo.putAll(pathInfo);\n        }\n    }\n\n    /**\n     * 设置默认输出路径\n     *\n     * @param globalConfig   全局配置\n     * @param strategyConfig 模板配置\n     */\n    private void setDefaultPathInfo(InjectionConfig injectionConfig, GlobalConfig globalConfig, StrategyConfig strategyConfig) {\n        Entity entity = strategyConfig.entity();\n        if (entity.isGenerate()) {\n            putPathInfo(injectionConfig, globalConfig.isKotlin() ? entity.getKotlinTemplate() : entity.getJavaTemplate(), OutputFile.entity, ConstVal.ENTITY);\n        }\n        Mapper mapper = strategyConfig.mapper();\n        if (mapper.isGenerateMapper()) {\n            putPathInfo(injectionConfig, mapper.getMapperTemplatePath(), OutputFile.mapper, ConstVal.MAPPER);\n        }\n        if (mapper.isGenerateMapperXml()) {\n            putPathInfo(injectionConfig, mapper.getMapperXmlTemplatePath(), OutputFile.xml, ConstVal.XML);\n        }\n        Service service = strategyConfig.service();\n        if (service.isGenerateService()) {\n            putPathInfo(injectionConfig, service.getServiceTemplate(), OutputFile.service, ConstVal.SERVICE);\n        }\n        if (service.isGenerateServiceImpl()) {\n            putPathInfo(injectionConfig, service.getServiceImplTemplate(), OutputFile.serviceImpl, ConstVal.SERVICE_IMPL);\n        }\n        Controller controller = strategyConfig.controller();\n        if (controller.isGenerate()) {\n            putPathInfo(injectionConfig, controller.getTemplatePath(), OutputFile.controller, ConstVal.CONTROLLER);\n        }\n        putPathInfo(injectionConfig, OutputFile.parent, ConstVal.PARENT);\n    }\n\n    private void putPathInfo(InjectionConfig injectionConfig, String template, OutputFile outputFile, String module) {\n        if (StringUtils.isNotBlank(template)) {\n            putPathInfo(injectionConfig, outputFile, module);\n        }\n    }\n\n    private void putPathInfo(InjectionConfig injectionConfig, OutputFile outputFile, String module) {\n        pathInfo.putIfAbsent(outputFile, joinPath(outputDir, packageConfig.getPackageInfo(injectionConfig, module)));\n    }\n\n    /**\n     * 连接路径字符串\n     *\n     * @param parentDir   路径常量字符串\n     * @param packageName 包名\n     * @return 连接后的路径\n     */\n    private String joinPath(String parentDir, String packageName) {\n        if (StringUtils.isBlank(parentDir)) {\n            parentDir = System.getProperty(ConstVal.JAVA_TMPDIR);\n        }\n        if (!StringUtils.endsWith(parentDir, File.separator)) {\n            parentDir += File.separator;\n        }\n        packageName = packageName.replaceAll(\"\\\\.\", StringPool.BACK_SLASH + File.separator);\n        return parentDir + packageName;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/Service.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.ITemplate;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.function.ConverterFileName;\nimport com.baomidou.mybatisplus.generator.util.ClassUtils;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Service属性配置\n *\n * @author nieqiurong 2020/10/11.\n * @since 3.5.0\n */\npublic class Service implements ITemplate {\n\n    private Service() {\n    }\n\n    /**\n     * 是否生成serviceImpl\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generateServiceImpl = true;\n\n\n    /**\n     * 是否生成service\n     *\n     * @since 3.5.6\n     */\n    @Getter\n    private boolean generateService = true;\n\n    /**\n     * @since 3.5.6\n     */\n    @Getter\n    private String serviceTemplate = ConstVal.TEMPLATE_SERVICE;\n\n    /**\n     * @since 3.5.6\n     */\n    @Getter\n    private String serviceImplTemplate = ConstVal.TEMPLATE_SERVICE_IMPL;\n\n    /**\n     * 自定义继承的Service类全称，带包名\n     */\n    private String superServiceClass = ConstVal.SUPER_SERVICE_CLASS;\n\n    /**\n     * 自定义继承的ServiceImpl类全称，带包名\n     */\n    private String superServiceImplClass = ConstVal.SUPER_SERVICE_IMPL_CLASS;\n\n    @NotNull\n    public String getSuperServiceClass() {\n        return superServiceClass;\n    }\n\n    @NotNull\n    public String getSuperServiceImplClass() {\n        return superServiceImplClass;\n    }\n\n    /**\n     * 转换输出Service文件名称\n     *\n     * @since 3.5.0\n     */\n    private ConverterFileName converterServiceFileName = (entityName -> \"I\" + entityName + ConstVal.SERVICE);\n\n    /**\n     * 转换输出ServiceImpl文件名称\n     *\n     * @since 3.5.0\n     */\n    private ConverterFileName converterServiceImplFileName = (entityName -> entityName + ConstVal.SERVICE_IMPL);\n\n    /**\n     * 是否覆盖已有文件（默认 false）\n     *\n     * @since 3.5.2\n     */\n    @Getter\n    private boolean fileOverride;\n\n    @NotNull\n    public ConverterFileName getConverterServiceFileName() {\n        return converterServiceFileName;\n    }\n\n    @NotNull\n    public ConverterFileName getConverterServiceImplFileName() {\n        return converterServiceImplFileName;\n    }\n\n    @Override\n    @NotNull\n    public Map<String, Object> renderData(@NotNull TableInfo tableInfo) {\n        Map<String, Object> data = new HashMap<>();\n        data.put(\"superServiceClassPackage\", this.superServiceClass);\n        data.put(\"superServiceClass\", ClassUtils.getSimpleName(this.superServiceClass));\n        data.put(\"superServiceImplClassPackage\", this.superServiceImplClass);\n        data.put(\"superServiceImplClass\", ClassUtils.getSimpleName(this.superServiceImplClass));\n        data.put(\"generateServiceImpl\", this.generateServiceImpl);\n        data.put(\"generateService\", this.generateService);\n        return data;\n    }\n\n    public static class Builder extends BaseBuilder {\n\n        private final Service service = new Service();\n\n        public Builder(@NotNull StrategyConfig strategyConfig) {\n            super(strategyConfig);\n        }\n\n        /**\n         * Service接口父类\n         *\n         * @param clazz 类\n         * @return this\n         */\n        public Builder superServiceClass(@NotNull Class<?> clazz) {\n            return superServiceClass(clazz.getName());\n        }\n\n        /**\n         * Service接口父类\n         *\n         * @param superServiceClass 类名\n         * @return this\n         */\n        public Builder superServiceClass(@NotNull String superServiceClass) {\n            this.service.superServiceClass = superServiceClass;\n            return this;\n        }\n\n        /**\n         * Service实现类父类\n         *\n         * @param clazz 类\n         * @return this\n         */\n        public Builder superServiceImplClass(@NotNull Class<?> clazz) {\n            return superServiceImplClass(clazz.getName());\n        }\n\n        /**\n         * Service实现类父类\n         *\n         * @param superServiceImplClass 类名\n         * @return this\n         */\n        public Builder superServiceImplClass(@NotNull String superServiceImplClass) {\n            this.service.superServiceImplClass = superServiceImplClass;\n            return this;\n        }\n\n        /**\n         * 转换输出service接口文件名称\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertServiceFileName(@NotNull ConverterFileName converter) {\n            this.service.converterServiceFileName = converter;\n            return this;\n        }\n\n        /**\n         * 转换输出service实现类文件名称\n         *\n         * @param converter 　转换处理\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder convertServiceImplFileName(@NotNull ConverterFileName converter) {\n            this.service.converterServiceImplFileName = converter;\n            return this;\n        }\n\n        /**\n         * 格式化service接口文件名称\n         *\n         * @param format 　格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatServiceFileName(@NotNull String format) {\n            return convertServiceFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 格式化service实现类文件名称\n         *\n         * @param format 　格式\n         * @return this\n         * @since 3.5.0\n         */\n        public Builder formatServiceImplFileName(@NotNull String format) {\n            return convertServiceImplFileName((entityName) -> String.format(format, entityName));\n        }\n\n        /**\n         * 覆盖已有文件（该方法后续会删除，替代方法为enableFileOverride方法）\n         *\n         * @see #enableFileOverride()\n         */\n        @Deprecated\n        public Builder fileOverride() {\n            this.service.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 覆盖已有文件\n         */\n        public Builder enableFileOverride() {\n            this.service.fileOverride = true;\n            return this;\n        }\n\n        /**\n         * 禁用生成Service\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disable() {\n            this.service.generateService = false;\n            this.service.generateServiceImpl = false;\n            return this;\n        }\n\n        /**\n         * 禁用生成\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disableService() {\n            this.service.generateService = false;\n            return this;\n        }\n\n        /**\n         * 禁用生成ServiceImpl\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder disableServiceImpl() {\n            this.service.generateServiceImpl = false;\n            return this;\n        }\n\n        /**\n         * Service模板路径\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder serviceTemplate(@NotNull String template) {\n            this.service.serviceTemplate = template;\n            return this;\n        }\n\n        /**\n         * ServiceImpl模板路径\n         *\n         * @return this\n         * @since 3.5.6\n         */\n        public Builder serviceImplTemplate(@NotNull String template) {\n            this.service.serviceImplTemplate = template;\n            return this;\n        }\n\n        @NotNull\n        public Service get() {\n            return this.service;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/builder/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器，构建类\n */\npackage com.baomidou.mybatisplus.generator.config.builder;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/ClickHouseTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.MAP;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.OBJECT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * ClickHouse 字段类型转换\n *\n * @author urzeye\n * @since 2021年9月12日\n */\npublic class ClickHouseTypeConvert implements ITypeConvert {\n\n    public static final ClickHouseTypeConvert INSTANCE = new ClickHouseTypeConvert();\n\n    static final String[] INTEGER_TYPE = new String[]{\"intervalyear\", \"intervalquarter\", \"intervalmonth\", \"intervalweek\", \"intervalday\", \"intervalhour\", \"intervalminute\", \"intervalsecond\", \"uint16\", \"uint8\", \"int16\", \"int8\", \"int32\"};\n\n    static final String[] BIGINTEGER_TYPE = new String[]{\"uint256\", \"uint128\", \"uint64\", \"int256\", \"int128\"};\n\n    static final String[] BIGDECIMAL_TYPE = new String[]{\"decimal32\", \"decimal64\", \"decimal128\", \"decimal256\", \"decimal\"};\n\n    static final String[] LONG_TYPE = new String[]{\"int64\", \"uint32\"};\n\n    static final String[] STRING_TYPE = new String[]{\"uuid\", \"char\", \"varchar\", \"text\", \"tinytext\", \"longtext\", \"blob\", \"tinyblob\", \"mediumblob\", \"longblob\", \"enum8\", \"enum16\", \"ipv4\", \"ipv6\", \"string\", \"fixedstring\", \"nothing\", \"nested\", \"tuple\", \"aggregatefunction\", \"unknown\"};\n\n\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig globalConfig, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType).test(containsAny(INTEGER_TYPE).then(INTEGER)).test(containsAny(BIGINTEGER_TYPE).then(BIG_INTEGER)).test(containsAny(BIGDECIMAL_TYPE).then(BIG_DECIMAL)).test(containsAny(LONG_TYPE).then(LONG)).test(contains(\"float32\").then(FLOAT)).test(contains(\"float64\").then(DOUBLE)).test(contains(\"map\").then(MAP)).test(contains(\"array\").then(OBJECT)).test(containsAny(\"date\", \"datetime\", \"datetime64\").then(t -> toDateType(globalConfig, fieldType))).test(containsAny(STRING_TYPE).then(STRING)).or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String type) {\n        switch (config.getDateType()) {\n            case SQL_PACK:\n                if (\"date\".equals(type)) {\n                    return DbColumnType.DATE_SQL;\n                }\n                return DbColumnType.TIMESTAMP;\n            case TIME_PACK:\n                if (\"date\".equals(type)) {\n                    return DbColumnType.LOCAL_DATE;\n                }\n                return DbColumnType.LOCAL_DATE_TIME;\n            default:\n                return DbColumnType.DATE;\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/DB2TypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BASE_SHORT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * DB2 字段类型转换\n *\n * @author zhanyao, hanchunlin\n * @since 2018-05-16\n */\npublic class DB2TypeConvert implements ITypeConvert {\n\n    public static final DB2TypeConvert INSTANCE = new DB2TypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"text\", \"json\", \"enum\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(contains(\"smallint\").then(BASE_SHORT))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\", \"year\").then(DATE))\n            .test(contains(\"bit\").then(BOOLEAN))\n            .test(contains(\"decimal\").then(BIG_DECIMAL))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BLOB))\n            .test(contains(\"binary\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .or(STRING);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/DmTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * DM 字段类型转换\n *\n * @author halower, hanchunlin, daiby\n * @since 2019-06-27\n */\npublic class DmTypeConvert implements ITypeConvert {\n\n    public static final DmTypeConvert INSTANCE = new DmTypeConvert();\n\n    /**\n     * 字符数据类型: CHAR,CHARACTER,VARCHAR\n     * <p>\n     * 数值数据类型: NUMBER,NUMERIC,DECIMAL,DEC,MONEY,BIT,BOOL,BOOLEAN,INTEGER,INT,BIGINT,TINYINT,BYTE,SMALLINT,BINARY,\n     * VARBINARY\n     * <p>\n     * 近似数值数据类型: FLOAT\n     * <p>\n     * DOUBLE, DOUBLE PRECISION,REAL\n     * <p>\n     * 日期时间数据类型\n     * <p>\n     * 多媒体数据类型: TEXT,LONGVARCHAR,CLOB,BLOB,IMAGE\n     *\n     * @param config    全局配置\n     * @param fieldType 字段类型\n     * @return 对应的数据类型\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"text\").then(STRING))\n            .test(contains(\"number\").then(DmTypeConvert::toNumberType))\n            .test(containsAny(\"numeric\", \"dec\", \"money\").then(BIG_DECIMAL))\n            .test(containsAny(\"bit\", \"bool\").then(BOOLEAN))\n            .test(contains(\"bigint\").then(BIG_INTEGER))\n            .test(containsAny(\"int\", \"byte\").then(INTEGER))\n            .test(contains(\"binary\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(containsAny(\"double\", \"real\").then(DOUBLE))\n            .test(containsAny(\"date\", \"time\").then(DATE))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BLOB))\n            .test(contains(\"image\").then(BYTE_ARRAY))\n            .or(STRING);\n    }\n\n    private static IColumnType toNumberType(String typeName) {\n        if (typeName.matches(\"number\\\\([0-9]\\\\)\")) {\n            return DbColumnType.INTEGER;\n        } else if (typeName.matches(\"number\\\\(1[0-8]\\\\)\")) {\n            return DbColumnType.LONG;\n        }\n        return DbColumnType.BIG_DECIMAL;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/FirebirdTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.SHORT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * Firebird 数据库字段类型转换\n *\n * @author hubin, hanchunlin\n * @since 2017-01-20\n */\npublic class FirebirdTypeConvert implements ITypeConvert {\n\n    public static final FirebirdTypeConvert INSTANCE = new FirebirdTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"cstring\", \"text\").then(STRING))\n            .test(contains(\"short\").then(SHORT))\n            .test(contains(\"long\").then(LONG))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .test(contains(\"blob\").then(BLOB))\n            .test(contains(\"int64\").then(LONG))\n            .test(containsAny(\"date\", \"time\", \"year\").then(t -> toDateType(config, t)))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String type) {\n        switch (config.getDateType()) {\n            case ONLY_DATE:\n                return DbColumnType.DATE;\n            case SQL_PACK:\n                switch (type) {\n                    case \"date\":\n                    case \"year\":\n                        return DbColumnType.DATE_SQL;\n                    case \"time\":\n                        return DbColumnType.TIME;\n                    default:\n                        return DbColumnType.TIMESTAMP;\n                }\n            case TIME_PACK:\n                switch (type) {\n                    case \"date\":\n                        return DbColumnType.LOCAL_DATE;\n                    case \"time\":\n                        return DbColumnType.LOCAL_TIME;\n                    case \"year\":\n                        return DbColumnType.YEAR;\n                    default:\n                        return DbColumnType.LOCAL_DATE_TIME;\n                }\n        }\n        return STRING;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/GaussDBSqlTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.OBJECT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * GaussDB字段类型转换\n *\n * @author nieqiurong\n * @since 3.5.13\n */\npublic class GaussDBSqlTypeConvert implements ITypeConvert {\n\n    public static final GaussDBSqlTypeConvert INSTANCE = new GaussDBSqlTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"json\", \"uuid\").then(OBJECT))\n            .test(containsAny(\"char\", \"text\", \"enum\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\").then(t -> toDateType(config, t)))\n            .test(contains(\"bit\").then(BOOLEAN))\n            .test(containsAny(\"decimal\", \"numeric\").then(BIG_DECIMAL))\n            .test(contains(\"bytea\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .test(contains(\"boolean\").then(BOOLEAN))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String type) {\n        switch (config.getDateType()) {\n            case SQL_PACK:\n                switch (type) {\n                    case \"date\":\n                        return DbColumnType.DATE_SQL;\n                    case \"time\":\n                        return DbColumnType.TIME;\n                    default:\n                        return DbColumnType.TIMESTAMP;\n                }\n            case TIME_PACK:\n                switch (type) {\n                    case \"date\":\n                        return DbColumnType.LOCAL_DATE;\n                    case \"time\":\n                        return DbColumnType.LOCAL_TIME;\n                    default:\n                        return DbColumnType.LOCAL_DATE_TIME;\n                }\n            default:\n                return DbColumnType.DATE;\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/KingbaseESTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE_SQL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIMESTAMP;\n\n/**\n * KingbaseES 字段类型转换\n *\n * @author kingbase, hanchunlin\n * @since 2019-10-12\n */\npublic class KingbaseESTypeConvert implements ITypeConvert {\n\n    public static final KingbaseESTypeConvert INSTANCE = new KingbaseESTypeConvert();\n\n    /**\n     * @param globalConfig 全局配置\n     * @param fieldType    字段类型\n     * @return 返回对应的字段类型\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig globalConfig, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"text\", \"json\", \"enum\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\").then(p -> toDateType(globalConfig, p)))\n            .test(containsAny(\"bit\", \"boolean\").then(BOOLEAN))\n            .test(containsAny(\"decimal\", \"numeric\").then(BIG_DECIMAL))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    private IColumnType toDateType(GlobalConfig config, String type) {\n        DateType dateType = config.getDateType();\n        if (dateType == DateType.SQL_PACK) {\n            switch (type) {\n                case \"date\":\n                    return DATE_SQL;\n                case \"time\":\n                    return TIME;\n                default:\n                    return TIMESTAMP;\n            }\n        } else if (dateType == DateType.TIME_PACK) {\n            switch (type) {\n                case \"date\":\n                    return LOCAL_DATE;\n                case \"time\":\n                    return LOCAL_TIME;\n                default:\n                    return LOCAL_DATE_TIME;\n            }\n        }\n        return DbColumnType.DATE;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/MySqlTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * MYSQL 数据库字段类型转换\n * bit类型数据转换 bit(1) -> Boolean类型  bit(2->64)  -> Byte类型\n *\n * @author hubin, hanchunlin, xiaoliang\n * @since 2017-01-20\n */\npublic class MySqlTypeConvert implements ITypeConvert {\n\n    public static final MySqlTypeConvert INSTANCE = new MySqlTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(contains(\"point\").then(BYTE_ARRAY))\n            .test(containsAny(\"char\", \"text\", \"json\", \"enum\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(containsAny(\"tinyint(1)\", \"bit(1)\").then(BOOLEAN))\n            .test(contains(\"bit\").then(BYTE))\n            .test(contains(\"int\").then(INTEGER))\n            .test(contains(\"decimal\").then(BIG_DECIMAL))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BLOB))\n            .test(contains(\"binary\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .test(containsAny(\"date\", \"time\", \"year\").then(t -> toDateType(config, t)))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String type) {\n        String dateType = type.replaceAll(\"\\\\(\\\\d+\\\\)\", \"\");\n        switch (config.getDateType()) {\n            case ONLY_DATE:\n                return DbColumnType.DATE;\n            case SQL_PACK:\n                switch (dateType) {\n                    case \"date\":\n                    case \"year\":\n                        return DbColumnType.DATE_SQL;\n                    case \"time\":\n                        return DbColumnType.TIME;\n                    default:\n                        return DbColumnType.TIMESTAMP;\n                }\n            case TIME_PACK:\n                switch (dateType) {\n                    case \"date\":\n                        return DbColumnType.LOCAL_DATE;\n                    case \"time\":\n                        return DbColumnType.LOCAL_TIME;\n                    case \"year\":\n                        return DbColumnType.YEAR;\n                    default:\n                        return DbColumnType.LOCAL_DATE_TIME;\n                }\n        }\n        return STRING;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/OracleTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * Oracle 数据库生成对应实体类时字段类型转换，跟据 Oracle 中的数据类型，返回对应的 Java 类型\n *\n * @author hubin, hanchunlin\n * @since 2017-01-20\n */\npublic class OracleTypeConvert implements ITypeConvert {\n\n    public static final OracleTypeConvert INSTANCE = new OracleTypeConvert();\n\n    /**\n     * 处理类型转换\n     *\n     * @param config    全局配置\n     * @param fieldType 字段类型\n     * @return 返回的对应的列类型\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"clob\").then(STRING))\n            .test(containsAny(\"date\", \"timestamp\").then(p -> toDateType(config)))\n            .test(contains(\"number\").then(OracleTypeConvert::toNumberType))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"blob\").then(BLOB))\n            .test(containsAny(\"binary\", \"raw\").then(BYTE_ARRAY))\n            .or(STRING);\n    }\n\n    /**\n     * 将对应的类型名称转换为对应的 java 类类型\n     * <p>\n     * String.valueOf(Integer.MAX_VALUE).length() == 10\n     * Integer 不一定能装下 10 位的数字\n     * <p>\n     * String.valueOf(Long.MAX_VALUE).length() == 19\n     * Long 不一定能装下 19 位的数字\n     *\n     * @param typeName 类型名称\n     * @return 返回列类型\n     */\n    private static IColumnType toNumberType(String typeName) {\n        if (typeName.matches(\"number\\\\([0-9]\\\\)\")) {\n            return DbColumnType.INTEGER;\n        } else if (typeName.matches(\"number\\\\(1[0-8]\\\\)\")) {\n            return DbColumnType.LONG;\n        }\n        return DbColumnType.BIG_DECIMAL;\n    }\n\n    /**\n     * 当前时间为字段类型，根据全局配置返回对应的时间类型\n     *\n     * @param config 全局配置\n     * @return 时间类型\n     * @see GlobalConfig#getDateType()\n     */\n    protected static IColumnType toDateType(GlobalConfig config) {\n        switch (config.getDateType()) {\n            case ONLY_DATE:\n                return DbColumnType.DATE;\n            case SQL_PACK:\n                return DbColumnType.TIMESTAMP;\n            case TIME_PACK:\n                return DbColumnType.LOCAL_DATE_TIME;\n            default:\n                return STRING;\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/OscarTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE_SQL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIMESTAMP;\n\n/**\n * Oscar数据库字段类型转换\n *\n * @author kingbase, hanchunlin\n * @since 2019-10-12\n */\npublic class OscarTypeConvert implements ITypeConvert {\n\n    public static final OscarTypeConvert INSTANCE = new OscarTypeConvert();\n\n    /**\n     * @param globalConfig 全局配置\n     * @param fieldType    字段类型\n     * @return 返回对应的字段类型\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig globalConfig, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"CHARACTER\", \"char\", \"varchar\", \"text\", \"character varying\").then(STRING))\n            .test(containsAny(\"bigint\", \"int8\").then(LONG))\n            .test(containsAny(\"int\", \"int1\", \"int2\", \"int3\", \"int4\", \"tinyint\", \"integer\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\", \"timestamp\").then(p -> toDateType(globalConfig, p)))\n            .test(containsAny(\"bit\", \"boolean\").then(BOOLEAN))\n            .test(containsAny(\"decimal\", \"numeric\", \"number\").then(BIG_DECIMAL))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(containsAny(\"double\", \"real\", \"float4\", \"float8\").then(DOUBLE))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    private IColumnType toDateType(GlobalConfig config, String type) {\n        DateType dateType = config.getDateType();\n        if (dateType == DateType.SQL_PACK) {\n            switch (type) {\n                case \"date\":\n                    return DATE_SQL;\n                case \"time\":\n                    return TIME;\n                default:\n                    return TIMESTAMP;\n            }\n        } else if (dateType == DateType.TIME_PACK) {\n            switch (type) {\n                case \"date\":\n                    return LOCAL_DATE;\n                case \"time\":\n                    return LOCAL_TIME;\n                default:\n                    return LOCAL_DATE_TIME;\n            }\n        }\n        return DbColumnType.DATE;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/PostgreSqlTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * PostgreSQL 字段类型转换\n *\n * @author hubin, hanchunlin\n * @since 2017-01-20\n */\npublic class PostgreSqlTypeConvert implements ITypeConvert {\n\n    public static final PostgreSqlTypeConvert INSTANCE = new PostgreSqlTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"text\", \"json\", \"enum\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\").then(t -> toDateType(config, t)))\n            .test(contains(\"bit\").then(BOOLEAN))\n            .test(containsAny(\"decimal\", \"numeric\").then(BIG_DECIMAL))\n            .test(contains(\"bytea\").then(BYTE_ARRAY))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .test(contains(\"boolean\").then(BOOLEAN))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config 配置信息\n     * @param type   类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String type) {\n        switch (config.getDateType()) {\n            case SQL_PACK:\n                switch (type) {\n                    case \"date\":\n                        return DbColumnType.DATE_SQL;\n                    case \"time\":\n                        return DbColumnType.TIME;\n                    default:\n                        return DbColumnType.TIMESTAMP;\n                }\n            case TIME_PACK:\n                switch (type) {\n                    case \"date\":\n                        return DbColumnType.LOCAL_DATE;\n                    case \"time\":\n                        return DbColumnType.LOCAL_TIME;\n                    default:\n                        return DbColumnType.LOCAL_DATE_TIME;\n                }\n            default:\n                return DbColumnType.DATE;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/SqlServerTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BYTE_ARRAY;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE_SQL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIMESTAMP;\n\n/**\n * SQLServer 字段类型转换\n *\n * @author hubin, hanchunlin\n * @since 2017-01-20\n */\npublic class SqlServerTypeConvert implements ITypeConvert {\n\n    public static final SqlServerTypeConvert INSTANCE = new SqlServerTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(containsAny(\"char\", \"xml\", \"text\").then(STRING))\n            .test(contains(\"bigint\").then(LONG))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"date\", \"time\").then(t -> toDateType(config, t)))\n            .test(contains(\"bit\").then(BOOLEAN))\n            .test(containsAny(\"decimal\", \"numeric\").then(DOUBLE))\n            .test(contains(\"money\").then(BIG_DECIMAL))\n            .test(containsAny(\"binary\", \"image\").then(BYTE_ARRAY))\n            .test(containsAny(\"float\", \"real\").then(FLOAT))\n            .or(STRING);\n    }\n\n    /**\n     * 转换为日期类型\n     *\n     * @param config    配置信息\n     * @param fieldType 类型\n     * @return 返回对应的列类型\n     */\n    public static IColumnType toDateType(GlobalConfig config, String fieldType) {\n        switch (config.getDateType()) {\n            case SQL_PACK:\n                switch (fieldType) {\n                    case \"date\":\n                        return DATE_SQL;\n                    case \"time\":\n                        return TIME;\n                    default:\n                        return TIMESTAMP;\n                }\n            case TIME_PACK:\n                switch (fieldType) {\n                    case \"date\":\n                        return LOCAL_DATE;\n                    case \"time\":\n                        return LOCAL_TIME;\n                    default:\n                        return LOCAL_DATE_TIME;\n                }\n            default:\n                return DATE;\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/SqliteTypeConvert.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert.toDateType;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.contains;\nimport static com.baomidou.mybatisplus.generator.config.converts.TypeConverts.containsAny;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.CLOB;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DOUBLE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\n\n/**\n * SQLite 字段类型转换\n *\n * @author chen_wj, hanchunlin\n * @since 2019-05-08\n */\npublic class SqliteTypeConvert implements ITypeConvert {\n\n    public static final SqliteTypeConvert INSTANCE = new SqliteTypeConvert();\n\n    /**\n     * {@inheritDoc}\n     *\n     * @see MySqlTypeConvert#toDateType(GlobalConfig, String)\n     */\n    @Override\n    public IColumnType processTypeConvert(@NotNull GlobalConfig config, @NotNull String fieldType) {\n        return TypeConverts.use(fieldType)\n            .test(contains(\"bigint\").then(LONG))\n            .test(containsAny(\"tinyint(1)\", \"boolean\").then(BOOLEAN))\n            .test(contains(\"int\").then(INTEGER))\n            .test(containsAny(\"text\", \"char\", \"enum\").then(STRING))\n            .test(containsAny(\"decimal\", \"numeric\").then(BIG_DECIMAL))\n            .test(contains(\"clob\").then(CLOB))\n            .test(contains(\"blob\").then(BLOB))\n            .test(contains(\"float\").then(FLOAT))\n            .test(contains(\"double\").then(DOUBLE))\n            .test(containsAny(\"date\", \"time\", \"year\").then(t -> toDateType(config, t)))\n            .or(DbColumnType.STRING);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/TypeConverts.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.converts.select.BranchBuilder;\nimport com.baomidou.mybatisplus.generator.config.converts.select.Selector;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\n\n/**\n * 该注册器负责注册并查询类型注册器\n *\n * @author nieqiuqiu, hanchunlin\n * @since 3.3.1\n */\npublic class TypeConverts {\n\n    /**\n     * 查询数据库类型对应的类型转换器\n     *\n     * @param dbType 数据库类型\n     * @return 返回转换器\n     */\n    public static ITypeConvert getTypeConvert(DbType dbType) {\n        switch (dbType) {\n            case ORACLE:\n                return OracleTypeConvert.INSTANCE;\n            case DB2:\n                return DB2TypeConvert.INSTANCE;\n            case DM:\n            case GAUSS:\n                return DmTypeConvert.INSTANCE;\n            case GAUSS_DB:\n                return GaussDBSqlTypeConvert.INSTANCE;\n            case KINGBASE_ES:\n                return KingbaseESTypeConvert.INSTANCE;\n            case OSCAR:\n                return OscarTypeConvert.INSTANCE;\n            case MYSQL:\n            case MARIADB:\n                return MySqlTypeConvert.INSTANCE;\n            case POSTGRE_SQL:\n                return PostgreSqlTypeConvert.INSTANCE;\n            case SQLITE:\n                return SqliteTypeConvert.INSTANCE;\n            case SQL_SERVER:\n                return SqlServerTypeConvert.INSTANCE;\n            case FIREBIRD:\n                return FirebirdTypeConvert.INSTANCE;\n            case CLICK_HOUSE:\n                return ClickHouseTypeConvert.INSTANCE;\n        }\n        return null;\n    }\n\n    /**\n     * 使用指定参数构建一个选择器\n     *\n     * @param param 参数\n     * @return 返回选择器\n     */\n    static Selector<String, IColumnType> use(String param) {\n        return new Selector<>(param.toLowerCase());\n    }\n\n    /**\n     * 这个分支构建器用于构建用于支持 {@link String#contains(CharSequence)} 的分支\n     *\n     * @param value 分支的值\n     * @return 返回分支构建器\n     * @see #containsAny(CharSequence...)\n     */\n    static BranchBuilder<String, IColumnType> contains(CharSequence value) {\n        return BranchBuilder.of(s -> s.contains(value));\n    }\n\n    /**\n     * @see #contains(CharSequence)\n     */\n    static BranchBuilder<String, IColumnType> containsAny(CharSequence... values) {\n        return BranchBuilder.of(s -> {\n            for (CharSequence value : values) {\n                if (s.contains(value)) {\n                    return true;\n                }\n            }\n            return false;\n        });\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/select/Branch.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts.select;\n\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * 分支提供者\n *\n * @author hanchunlin\n * Created at 2020/6/11 17:19\n * @see BranchBuilder\n */\npublic interface Branch<P, T> {\n\n    /**\n     * @return 分支进入条件\n     */\n    Predicate<P> tester();\n\n    /**\n     * @return 值工厂\n     */\n    Function<P, T> factory();\n\n    /**\n     * 工厂方法，快速创建分支\n     *\n     * @param tester  测试器\n     * @param factory 值工厂\n     * @param <P>     参数类型\n     * @param <T>     值类型\n     * @return 返回一个新的分支\n     */\n    static <P, T> Branch<P, T> of(Predicate<P> tester, Function<P, T> factory) {\n        return new Branch<P, T>() {\n\n            @Override\n            public Predicate<P> tester() {\n                return tester;\n            }\n\n            @Override\n            public Function<P, T> factory() {\n                return factory;\n            }\n\n        };\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/select/BranchBuilder.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts.select;\n\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\n/**\n * 分支构建者\n *\n * @author hanchunlin\n * Created at 2020/6/11 17:22\n */\npublic interface BranchBuilder<P, T> {\n\n    /**\n     * 使用一个值工厂构造出一个分支\n     *\n     * @param factory 值工厂\n     * @return 返回分支\n     */\n    Branch<P, T> then(Function<P, T> factory);\n\n    /**\n     * 从值构建出一个分支\n     *\n     * @param value 值\n     * @return 返回一个分支\n     */\n    default Branch<P, T> then(T value) {\n        return then(p -> value);\n    }\n\n    /**\n     * 工厂函数，用于创建分支构建者\n     *\n     * @param tester 测试器\n     * @param <P>    参数类型\n     * @param <T>    返回值类型\n     * @return 返回一个分支创建者\n     */\n    static <P, T> BranchBuilder<P, T> of(Predicate<P> tester) {\n        return factory -> Branch.of(tester, factory);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/converts/select/Selector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.converts.select;\n\nimport lombok.Getter;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\n/**\n * 分支结果选择器\n * <p>\n * 当前选择器会从给定的分支中选择第一个匹配的分支，并返回其结果\n * <p>\n * 一旦结果被选择，其他的分支将不再被调用\n *\n * @author hanchunlin\n * Created at 2020/6/11 16:55\n */\npublic class Selector<P, T> {\n    /**\n     *  当前选择器是否已经选择分支，如果已经存在分支被击中，则返回 true；否则返回 false\n     */\n    @Getter\n    private boolean selected = false;\n    private Function<P, T> factory;\n\n    /**\n     * 选择器参数，该参数会在进行条件判断和结果获取时会被当做条件传入\n     */\n    private final P param;\n\n    /**\n     * @param param 参数\n     */\n    public Selector(P param) {\n        this.param = param;\n    }\n\n    /**\n     * 使用指定的参数创建选择器\n     *\n     * @param param 参数\n     * @param <P>   参数类型\n     * @param <T>   返回值类型\n     * @return 返回新的选择器\n     */\n    public static <P, T> Selector<P, T> param(P param) {\n        return new Selector<>(param);\n    }\n\n    /**\n     * 传入一个新的分支，如果这个分支满足条件\n     *\n     * @param branch 则当前选择器将接受当前分支的结果并完成\n     * @return 选择器自身\n     */\n    public Selector<P, T> test(Branch<P, T> branch) {\n        if (!selected) {\n            boolean pass = branch.tester().test(param);\n            if (pass) {\n                selected = true;\n                factory = branch.factory();\n            }\n        }\n        return this;\n    }\n\n    /**\n     * 获取结果，如果当前选择器没有击中任何条件分支，则从给定的提供者中获取结果；\n     * 否则将使用当前选择器选中的分支\n     *\n     * @param supplier 默认值提供者\n     * @return 如果有分支被击中，则返回分支值，否则返回参数提供的值\n     */\n    public T or(Supplier<T> supplier) {\n        return selected ? this.factory.apply(param) : supplier.get();\n    }\n\n    /**\n     * @param t 给定默认值\n     * @return 如果有分支被击中，则返回分支值，否则返回参数\n     * @see #or(Supplier)\n     */\n    public T or(T t) {\n        return or(() -> t);\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"Selector{success=%s}\", selected);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器，配置相关类\n */\npackage com.baomidou.mybatisplus.generator.config;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/po/LikeTable.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.po;\n\nimport com.baomidou.mybatisplus.core.enums.SqlLike;\nimport com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;\n\n/**\n * 表名拼接\n *\n * @author nieqiuqiu\n * @since 3.3.0\n */\npublic class LikeTable {\n\n    private final String value;\n\n    private SqlLike like = SqlLike.DEFAULT;\n\n    public LikeTable(String value) {\n        this.value = value;\n    }\n\n    public LikeTable(String value, SqlLike like) {\n        this.value = value;\n        this.like = like;\n    }\n\n    @Override\n    public String toString() {\n        return getValue();\n    }\n\n    public String getValue() {\n        return SqlUtils.concatLike(this.value, like);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableField.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.po;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.IKeyWordsHandler;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport com.baomidou.mybatisplus.generator.fill.Column;\nimport com.baomidou.mybatisplus.generator.fill.Property;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport com.baomidou.mybatisplus.generator.model.AnnotationAttributes;\nimport org.apache.ibatis.type.JdbcType;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * 表字段信息\n *\n * @author YangHu\n * @since 2016-12-03\n */\npublic class TableField {\n\n    /**\n     * 是否做注解转换\n     */\n    private boolean convert;\n\n    /**\n     * 是否主键\n     */\n    private boolean keyFlag;\n\n    /**\n     * 主键是否为自增类型\n     */\n    private boolean keyIdentityFlag;\n\n    /**\n     * 字段名称\n     */\n    private final String name;\n\n    /**\n     * 字段类型（已弃用，使用 {@link #columnType} 代替）\n     */\n    @Deprecated\n    private String type;\n\n    /**\n     * 属性名称\n     */\n    private String propertyName;\n\n    /**\n     * 字段类型\n     */\n    private IColumnType columnType;\n\n    /**\n     * 字段注释\n     */\n    private String comment;\n\n    /**\n     * 填充\n     */\n    private String fill;\n\n    /**\n     * 是否关键字\n     *\n     * @since 3.3.2\n     */\n    private boolean keyWords;\n\n    /**\n     * 数据库字段（关键字含转义符号）\n     *\n     * @since 3.3.2\n     */\n    private String columnName;\n\n    /**\n     * 自定义查询字段列表\n     */\n    private Map<String, Object> customMap;\n\n    /**\n     * 字段元数据信息\n     *\n     * @since 3.5.0\n     */\n    private MetaInfo metaInfo;\n\n    /**\n     * 实体属性配置\n     */\n    private final Entity entity;\n\n    /**\n     * 数据库配置\n     */\n    private final DataSourceConfig dataSourceConfig;\n\n    /**\n     * 全局配置\n     */\n    private final GlobalConfig globalConfig;\n\n    /**\n     * 字段注解\n     *\n     * @since 3.5.10\n     */\n    private final List<AnnotationAttributes> annotationAttributesList = new ArrayList<>();\n\n    /**\n     * 字段注解处理\n     *\n     * @since 3.5.11\n     */\n    private Function<List<? extends AnnotationAttributes>, List<AnnotationAttributes>> annotationAttributesFunction;\n\n    /**\n     * 构造方法\n     *\n     * @param configBuilder 配置构建\n     * @param name          数据库字段名称\n     * @since 3.5.0\n     */\n    public TableField(@NotNull ConfigBuilder configBuilder, @NotNull String name) {\n        this.name = name;\n        this.columnName = name;\n        this.entity = configBuilder.getStrategyConfig().entity();\n        this.dataSourceConfig = configBuilder.getDataSourceConfig();\n        this.globalConfig = configBuilder.getGlobalConfig();\n    }\n\n    /**\n     * 设置属性名称\n     *\n     * @param propertyName 属性名\n     * @param columnType   字段类型\n     * @return this\n     * @see #setColumnType(IColumnType)\n     * @see #setPropertyName(String)\n     * @since 3.5.0\n     * @deprecated 3.5.15\n     */\n    @Deprecated\n    public TableField setPropertyName(@NotNull String propertyName, @NotNull IColumnType columnType) {\n        return setColumnType(columnType).setPropertyName(propertyName);\n    }\n\n    /**\n     * 设置属性名称\n     *\n     * @param propertyName 属性名\n     * @return this\n     * @since 3.5.15\n     */\n    public TableField setPropertyName(@NotNull String propertyName) {\n        if (entity.isBooleanColumnRemoveIsPrefix()\n            && \"boolean\".equalsIgnoreCase(this.getPropertyType()) && propertyName.startsWith(\"is\")) {\n            this.convert = true;\n            this.propertyName = StringUtils.removePrefixAfterPrefixToLower(propertyName, 2);\n            return this;\n        }\n        // 下划线转驼峰策略\n        if (NamingStrategy.underline_to_camel.equals(this.entity.getColumnNaming())) {\n            this.convert = !propertyName.equalsIgnoreCase(NamingStrategy.underlineToCamel(this.columnName));\n        }\n        // 原样输出策略\n        if (NamingStrategy.no_change.equals(this.entity.getColumnNaming())) {\n            this.convert = !propertyName.equalsIgnoreCase(this.columnName);\n        }\n        if (entity.isTableFieldAnnotationEnable()) {\n            this.convert = true;\n        } else {\n            if (this.keyFlag) {\n                this.convert = !ConstVal.DEFAULT_ID_NAME.equals(propertyName);\n            }\n        }\n        this.propertyName = propertyName;\n        return this;\n    }\n\n\n    /**\n     * 指定字段类型\n     *\n     * @param columnType 字段类型\n     * @return this\n     * @since 3.5.15\n     */\n    public TableField setColumnType(@NotNull IColumnType columnType) {\n        this.columnType = columnType;\n        return this;\n    }\n\n    public String getPropertyType() {\n        if (null != columnType) {\n            return columnType.getType();\n        }\n        return null;\n    }\n\n    /**\n     * 按 JavaBean 规则来生成 get 和 set 方法后面的属性名称\n     * 需要处理一下特殊情况：\n     * <p>\n     * 1、如果只有一位，转换为大写形式\n     * 2、如果多于 1 位，只有在第二位是小写的情况下，才会把第一位转为小写\n     * <p>\n     * 我们并不建议在数据库对应的对象中使用基本类型，因此这里不会考虑基本类型的情况\n     */\n    public String getCapitalName() {\n        if (propertyName.length() == 1) {\n            return propertyName.toUpperCase();\n        }\n        if (Character.isLowerCase(propertyName.charAt(1))) {\n            return Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);\n        }\n        return propertyName;\n    }\n\n    /**\n     * 获取注解字段名称\n     *\n     * @return 字段\n     * @since 3.3.2\n     */\n    public String getAnnotationColumnName() {\n        if (keyWords) {\n            if (columnName.startsWith(\"\\\"\")) {\n                return String.format(\"\\\\\\\"%s\\\\\\\"\", name);\n            }\n        }\n        return columnName;\n    }\n\n    /**\n     * 是否为乐观锁字段\n     *\n     * @return 是否为乐观锁字段\n     * @since 3.5.0\n     */\n    public boolean isVersionField() {\n        String propertyName = entity.getVersionPropertyName();\n        String columnName = entity.getVersionColumnName();\n        return StringUtils.isNotBlank(propertyName) && this.propertyName.equals(propertyName)\n            || StringUtils.isNotBlank(columnName) && this.name.equalsIgnoreCase(columnName);\n    }\n\n    /**\n     * 是否为逻辑删除字段\n     *\n     * @return 是否为逻辑删除字段\n     * @since 3.5.0\n     */\n    public boolean isLogicDeleteField() {\n        String propertyName = entity.getLogicDeletePropertyName();\n        String columnName = entity.getLogicDeleteColumnName();\n        return StringUtils.isNotBlank(propertyName) && this.propertyName.equals(propertyName)\n            || StringUtils.isNotBlank(columnName) && this.name.equalsIgnoreCase(columnName);\n    }\n\n    /**\n     * 设置主键\n     *\n     * @param autoIncrement 自增标识\n     * @return this\n     * @since 3.5.0\n     */\n    public TableField primaryKey(boolean autoIncrement) {\n        this.keyFlag = true;\n        this.keyIdentityFlag = autoIncrement;\n        return this;\n    }\n\n    /**\n     * @param type 类型\n     * @return this\n     */\n    public TableField setType(String type) {\n        this.type = type;\n        return this;\n    }\n\n    public TableField setComment(String comment) {\n        //TODO 待重构此处\n        this.comment = (this.globalConfig.isSwagger() || this.globalConfig.isSpringdoc())\n            && StringUtils.isNotBlank(comment) ? comment.replace(\"\\\"\", \"\\\\\\\"\") : comment;\n        return this;\n    }\n\n    public TableField setColumnName(String columnName) {\n        this.columnName = columnName;\n        IKeyWordsHandler keyWordsHandler = dataSourceConfig.getKeyWordsHandler();\n        if (keyWordsHandler != null && keyWordsHandler.isKeyWords(columnName)) {\n            this.keyWords = true;\n            this.columnName = keyWordsHandler.formatColumn(columnName);\n        }\n        return this;\n    }\n\n    public TableField setCustomMap(Map<String, Object> customMap) {\n        this.customMap = customMap;\n        return this;\n    }\n\n    public boolean isConvert() {\n        return convert;\n    }\n\n    public boolean isKeyFlag() {\n        return keyFlag;\n    }\n\n    public boolean isKeyIdentityFlag() {\n        return keyIdentityFlag;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public String getPropertyName() {\n        return propertyName;\n    }\n\n    public IColumnType getColumnType() {\n        return columnType;\n    }\n\n    public String getComment() {\n        return comment;\n    }\n\n    public String getFill() {\n        if (StringUtils.isBlank(fill)) {\n            entity.getTableFillList().stream()\n                //忽略大写字段问题\n                .filter(tf -> tf instanceof Column && tf.getName().equalsIgnoreCase(name)\n                    || tf instanceof Property && tf.getName().equals(propertyName))\n                .findFirst().ifPresent(tf -> this.fill = tf.getFieldFill().name());\n        }\n        return fill;\n    }\n\n    public boolean isKeyWords() {\n        return keyWords;\n    }\n\n    public String getColumnName() {\n        return columnName;\n    }\n\n    public Map<String, Object> getCustomMap() {\n        return customMap;\n    }\n\n    public MetaInfo getMetaInfo() {\n        return metaInfo;\n    }\n\n    public void setMetaInfo(MetaInfo metaInfo) {\n        this.metaInfo = metaInfo;\n    }\n\n    /**\n     * 获取实体配置信息\n     *\n     * @return 实体配置信息\n     * @since 3.5.10\n     */\n    public Entity getEntity() {\n        return this.entity;\n    }\n\n    /**\n     * 添加字段注解属性\n     *\n     * @param annotationAttributesList 注解属性集合\n     * @since 3.5.10\n     */\n    public void addAnnotationAttributesList(@NotNull List<AnnotationAttributes> annotationAttributesList) {\n        this.addAnnotationAttributesList(annotationAttributesList, null);\n    }\n\n    /**\n     * @param annotationAttributesList 注解属性集合\n     * @since 3.5.11\n     */\n    public void addAnnotationAttributesList(@NotNull List<AnnotationAttributes> annotationAttributesList, Function<List<? extends AnnotationAttributes>, List<AnnotationAttributes>> annotationAttributesFunction) {\n        this.annotationAttributesList.addAll(annotationAttributesList);\n        this.annotationAttributesFunction = annotationAttributesFunction;\n    }\n\n    /**\n     * 添加字段注解属性\n     *\n     * @param annotationAttributes 注解属性\n     * @since 3.5.10\n     */\n    public void addAnnotationAttributesList(@NotNull AnnotationAttributes annotationAttributes) {\n        this.annotationAttributesList.add(annotationAttributes);\n    }\n\n    /**\n     * 获取字段注解属性(默认按{@link AnnotationAttributes#getDisplayName()}长度进行升序)\n     *\n     * @return 字段注解属性\n     * @since 3.5.10\n     */\n    public List<AnnotationAttributes> getAnnotationAttributesList() {\n        return annotationAttributesFunction != null ? annotationAttributesFunction.apply(this.annotationAttributesList) : this.annotationAttributesList.stream()\n            .sorted(Comparator.comparingInt(s -> s.getDisplayName().length()))\n            .collect(Collectors.toList());\n    }\n\n    /**\n     * 元数据信息\n     *\n     * @author nieqiurong 2021/2/8\n     * @since 3.5.0\n     */\n    public static class MetaInfo {\n\n        /**\n         * 表名称\n         */\n        private String tableName;\n\n        /**\n         * 字段名称\n         */\n        private String columnName;\n\n        /**\n         * 字段长度\n         */\n        private int length;\n\n        /**\n         * 是否非空\n         */\n        private boolean nullable;\n\n        /**\n         * 字段注释\n         */\n        private String remarks;\n\n        /**\n         * 字段默认值\n         */\n        private String defaultValue;\n\n        /**\n         * 字段精度\n         */\n        private int scale;\n\n        /**\n         * JDBC类型\n         */\n        private JdbcType jdbcType;\n\n        /**\n         * 类型名称(可用做额外判断处理,例如在pg下,json,uuid,jsonb,tsquery这种都认为是OHTER 1111)\n         *\n         * @since 3.5.3\n         */\n        private String typeName;\n\n        /**\n         * 是否为生成列\n         *\n         * @since 3.5.8\n         */\n        private boolean generatedColumn;\n\n        public MetaInfo(DatabaseMetaDataWrapper.Column column, TableInfo tableInfo) {\n            if (column != null) {\n                this.tableName = tableInfo.getName();\n                this.columnName = column.getName();\n                this.length = column.getLength();\n                this.nullable = column.isNullable();\n                this.remarks = column.getRemarks();\n                this.defaultValue = column.getDefaultValue();\n                this.scale = column.getScale();\n                this.jdbcType = column.getJdbcType();\n                this.typeName = column.getTypeName();\n                this.generatedColumn = column.isGeneratedColumn();\n            }\n        }\n\n        public String getTableName() {\n            return tableName;\n        }\n\n        public String getColumnName() {\n            return columnName;\n        }\n\n        public int getLength() {\n            return length;\n        }\n\n        public boolean isNullable() {\n            return nullable;\n        }\n\n        public String getRemarks() {\n            return remarks;\n        }\n\n        public String getDefaultValue() {\n            return defaultValue;\n        }\n\n        public int getScale() {\n            return scale;\n        }\n\n        public JdbcType getJdbcType() {\n            return jdbcType;\n        }\n\n        public String getTypeName() {\n            return typeName;\n        }\n\n        @Override\n        public String toString() {\n            return \"MetaInfo{\" +\n                \"tableName='\" + tableName + '\\'' +\n                \", columnName='\" + columnName + '\\'' +\n                \", length=\" + length +\n                \", nullable=\" + nullable +\n                \", remarks='\" + remarks + '\\'' +\n                \", defaultValue='\" + defaultValue + '\\'' +\n                \", scale=\" + scale +\n                \", jdbcType=\" + jdbcType +\n                \", typeName='\" + typeName + '\\'' +\n                \", generatedColumn=\" + generatedColumn +\n                '}';\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableInfo.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.po;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.PackageConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.builder.Service;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n\n/**\n * 表信息，关联到当前字段信息\n *\n * @author YangHu, lanjerry\n * @since 2016/8/30\n */\npublic class TableInfo {\n\n    /**\n     * 策略配置\n     */\n    @Getter\n    private final StrategyConfig strategyConfig;\n\n    /**\n     * 全局配置信息\n     */\n    @Getter\n    private final GlobalConfig globalConfig;\n\n    /**\n     * 包导入信息\n     */\n    private final Set<String> importPackages = new TreeSet<>();\n\n    /**\n     * 是否转换\n     */\n    @Getter\n    private boolean convert;\n\n    /**\n     * 表名称\n     */\n    @Getter\n    private String name;\n\n    /**\n     * 表注释\n     */\n    @Getter\n    private String comment;\n\n    /**\n     * 实体名称\n     */\n    @Getter\n    private String entityName;\n\n    /**\n     * mapper名称\n     */\n    @Getter\n    private String mapperName;\n\n    /**\n     * xml名称\n     */\n    @Getter\n    private String xmlName;\n\n    /**\n     * service名称\n     */\n    @Getter\n    private String serviceName;\n\n    /**\n     * serviceImpl名称\n     */\n    @Getter\n    private String serviceImplName;\n\n    /**\n     * controller名称\n     */\n    @Getter\n    private String controllerName;\n\n    /**\n     * 表字段\n     */\n    private final List<TableField> fields = new ArrayList<>();\n\n    /**\n     * 是否有主键\n     */\n    @Getter\n    private boolean havePrimaryKey;\n\n    /**\n     * 公共字段\n     */\n    private final List<TableField> commonFields = new ArrayList<>();\n\n    /**\n     * 字段名称集\n     */\n    private String fieldNames;\n\n    /**\n     * 实体\n     */\n    private final Entity entity;\n\n    /**\n     * 索引信息\n     *\n     * @since 3.5.10\n     */\n    @Setter\n    @Getter\n    private List<DatabaseMetaDataWrapper.Index> indexList;\n\n    /**\n     * 字段信息\n     *\n     * @since 3.5.10\n     */\n    @Getter\n    private final Map<String, TableField> tableFieldMap = new HashMap<>();\n\n    /**\n     * @since 3.5.10\n     */\n    @Getter\n    @Setter\n    private String schemaName;\n\n    /**\n     * @since 3.5.11\n     */\n    @Getter\n    private PackageConfig packageConfig;\n\n    /**\n     * 构造方法\n     *\n     * @param configBuilder 配置构建\n     * @param name          表名\n     * @since 3.5.0\n     */\n    public TableInfo(@NotNull ConfigBuilder configBuilder, @NotNull String name) {\n        this.strategyConfig = configBuilder.getStrategyConfig();\n        this.globalConfig = configBuilder.getGlobalConfig();\n        this.entity = configBuilder.getStrategyConfig().entity();\n        this.packageConfig = configBuilder.getPackageConfig();\n        this.name = name;\n    }\n\n    /**\n     * @since 3.5.0\n     */\n    protected TableInfo setConvert() {\n        if (strategyConfig.startsWithTablePrefix(name) || entity.isTableFieldAnnotationEnable()) {\n            this.convert = true;\n        } else {\n            this.convert = !entityName.equalsIgnoreCase(name);\n        }\n        return this;\n    }\n\n    public String getEntityPath() {\n        return entityName.substring(0, 1).toLowerCase() + entityName.substring(1);\n    }\n\n    /**\n     * @param entityName 实体名称\n     * @return this\n     */\n    public TableInfo setEntityName(@NotNull String entityName) {\n        this.entityName = entityName;\n        setConvert();\n        return this;\n    }\n\n    /**\n     * 添加字段\n     *\n     * @param field 字段\n     * @since 3.5.0\n     */\n    public void addField(@NotNull TableField field) {\n        if (entity.matchIgnoreColumns(field.getColumnName())) {\n            // 忽略字段不在处理\n            return;\n        }\n        tableFieldMap.put(field.getName(), field);\n        if (entity.matchSuperEntityColumns(field.getColumnName())) {\n            this.commonFields.add(field);\n        } else {\n            this.fields.add(field);\n        }\n    }\n\n    /**\n     * @param pkgs 包空间\n     * @return this\n     * @since 3.5.0\n     */\n    public TableInfo addImportPackages(@NotNull String... pkgs) {\n        return addImportPackages(Arrays.asList(pkgs));\n    }\n\n    public TableInfo addImportPackages(@NotNull List<String> pkgList) {\n        importPackages.addAll(pkgList);\n        return this;\n    }\n\n    /**\n     * 转换filed实体为 xml mapper 中的 base column 字符串信息\n     */\n    public String getFieldNames() {\n        if (StringUtils.isBlank(fieldNames)) {\n            this.fieldNames = this.fields.stream().map(TableField::getColumnName).collect(Collectors.joining(\", \"));\n        }\n        return this.fieldNames;\n    }\n\n    /**\n     * 导包处理\n     *\n     * @since 3.5.0\n     */\n    public void importPackage() {\n        String superEntity = entity.getSuperClass();\n        if (StringUtils.isNotBlank(superEntity)) {\n            // 自定义父类\n            this.importPackages.add(superEntity);\n        } else {\n            if (entity.isActiveRecord()) {\n                // 无父类开启 AR 模式\n                this.importPackages.add(\"com.baomidou.mybatisplus.extension.activerecord.Model\");\n            }\n        }\n        if (entity.isSerialVersionUID() || entity.isActiveRecord()) {\n            this.importPackages.add(Serializable.class.getCanonicalName());\n            if (entity.isSerialAnnotation()) {\n                this.importPackages.add(\"java.io.Serial\");\n            }\n        }\n        if (this.isConvert()) {\n            this.importPackages.add(TableName.class.getCanonicalName());\n        }\n        IdType idType = entity.getIdType();\n        if (null != idType && this.isHavePrimaryKey()) {\n            // 指定需要 IdType 场景\n            this.importPackages.add(IdType.class.getCanonicalName());\n            this.importPackages.add(TableId.class.getCanonicalName());\n        }\n        this.fields.forEach(field -> {\n            IColumnType columnType = field.getColumnType();\n            if (null != columnType && null != columnType.getPkg()) {\n                importPackages.add(columnType.getPkg());\n            }\n            if (field.isKeyFlag()) {\n                // 主键\n                if (field.isConvert() || field.isKeyIdentityFlag()) {\n                    importPackages.add(TableId.class.getCanonicalName());\n                }\n                // 自增\n                if (field.isKeyIdentityFlag()) {\n                    importPackages.add(IdType.class.getCanonicalName());\n                }\n            } else if (field.isConvert()) {\n                // 普通字段\n                importPackages.add(com.baomidou.mybatisplus.annotation.TableField.class.getCanonicalName());\n            }\n            if (null != field.getFill()) {\n                // 填充字段\n                importPackages.add(com.baomidou.mybatisplus.annotation.TableField.class.getCanonicalName());\n                //TODO 好像default的不用处理也行,这个做优化项目.\n                importPackages.add(FieldFill.class.getCanonicalName());\n            }\n            if (field.isVersionField()) {\n                this.importPackages.add(Version.class.getCanonicalName());\n            }\n            if (field.isLogicDeleteField()) {\n                this.importPackages.add(TableLogic.class.getCanonicalName());\n            }\n        });\n    }\n\n    /**\n     * 处理表信息(文件名与导包)\n     *\n     * @since 3.5.0\n     */\n    public void processTable() {\n        String entityName = entity.getNameConvert().entityNameConvert(this);\n        this.setEntityName(entity.getConverterFileName().convert(entityName));\n        this.mapperName = strategyConfig.mapper().getConverterMapperFileName().convert(entityName);\n        this.xmlName = strategyConfig.mapper().getConverterXmlFileName().convert(entityName);\n        this.serviceName = strategyConfig.service().getConverterServiceFileName().convert(entityName);\n        this.serviceImplName = strategyConfig.service().getConverterServiceImplFileName().convert(entityName);\n        this.controllerName = strategyConfig.controller().getConverterFileName().convert(entityName);\n        this.importPackage();\n    }\n\n    public TableInfo setComment(String comment) {\n        //TODO 待重构此处\n        this.comment = (this.globalConfig.isSwagger() || this.globalConfig.isSpringdoc())\n            && StringUtils.isNotBlank(comment) ? comment.replace(\"\\\"\", \"\\\\\\\"\") : comment;\n        return this;\n    }\n\n    public TableInfo setHavePrimaryKey(boolean havePrimaryKey) {\n        this.havePrimaryKey = havePrimaryKey;\n        return this;\n    }\n\n    @NotNull\n    public Set<String> getImportPackages() {\n        return importPackages;\n    }\n\n    public TableInfo setConvert(boolean convert) {\n        this.convert = convert;\n        return this;\n    }\n\n    @NotNull\n    public List<TableField> getFields() {\n        return fields;\n    }\n\n    @NotNull\n    public List<TableField> getCommonFields() {\n        return commonFields;\n    }\n\n    /**\n     * 获取是否生成service接口\n     * @deprecated {@link Service.Builder#disableService()}\n     */\n    @Deprecated\n    public boolean isServiceInterface() {\n        return globalConfig.isServiceInterface();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/po/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器，输出相关类\n */\npackage com.baomidou.mybatisplus.generator.config.po;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/AbstractDbQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.generator.config.IDbQuery;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * 表数据查询抽象类\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic abstract class AbstractDbQuery implements IDbQuery {\n\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return false;\n    }\n\n\n    @Override\n    public String[] fieldCustom() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/ClickHouseQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * ClickHouse 表数据查询\n *\n * @author gaosheng\n * @since 2021-03-10\n */\npublic class ClickHouseQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM system.tables WHERE 1=1 \";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"select * from system.columns where table='%s'\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"name\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"comment\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"name\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"type\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"comment\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"is_in_primary_key\";\n    }\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return \"1\".equals(results.getString(\"is_in_primary_key\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/DB2Query.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * DB2 表数据查询\n *\n * @author zhanyao\n * @since 2018-05-16\n */\npublic class DB2Query extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM SYSCAT.TABLES where tabschema='%s'\";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT * FROM syscat.columns WHERE tabschema='%s' AND tabname='%s'\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"TABNAME\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"REMARKS\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"COLNAME\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"TYPENAME\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"REMARKS\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"IDENTITY\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/DMQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n\n/**\n * DM 表数据查询\n *\n * @author halower,daiby\n * @since 2019-06-27\n */\npublic class DMQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM ( SELECT /*+ MAX_OPT_N_TABLES(5) */ NULL AS TABLE_CAT, SCHEMAS.NAME AS TABLE_SCHEM, \\n\" +\n            \"TABS.NAME AS TABLE_NAME, CASE TABS.SUBTYPE$ WHEN 'UTAB' THEN 'TABLE' WHEN 'VIEW' THEN 'VIEW' WHEN 'STAB' THEN 'SYSTEM TABLE' WHEN 'SYNOM'\\n\" +\n            \" THEN 'SYNONYM' END AS TABLE_TYPE, (SELECT COMMENT$ FROM SYSTABLECOMMENTS WHERE SCHNAME = SCHEMAS.NAME AND TVNAME = TABS.NAME) AS REMARKS, \\n\" +\n            \" NULL AS TYPE_CAT, NULL AS TYPE_SCHEM, NULL AS TYPE_NAME, NULL AS SELF_REFERENCING_COL_NAME, NULL AS REF_GENERATION \\n\" +\n            \"  FROM (SELECT ID, NAME, PID FROM SYS.SYSOBJECTS WHERE TYPE$ = 'SCH' AND NAME LIKE '%s'  ESCAPE '!'  )  SCHEMAS, \\n\" +\n            \"  (SELECT * FROM SYS.SYSOBJECTS  WHERE ((SUBTYPE$='UTAB' AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 9 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) \\n\" +\n            \"  != 27 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 29 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 25 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT)\\n\" +\n            \"   != 12 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 7 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 21 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT)\\n\" +\n            \"    != 23 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 18 AND CAST((INFO3 & 0x00FF & 0x003F) AS INT) != 5)OR SUBTYPE$='VIEW' )) \\n\" +\n            \"TABS WHERE TABS.SCHID = SCHEMAS.ID ) TEMP WHERE 1=1\";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT /*+ MAX_OPT_N_TABLES(5) */ DISTINCT NULL AS TABLE_CAT, SCHS.NAME AS TABLE_SCHEM, TABS.NAME AS TABLE_NAME, COLS.NAME AS COLUMN_NAME, \\n\" +\n            \"CASE COLS.TYPE$ WHEN 'NUMBER' THEN 2 WHEN 'NUMERIC' THEN 2 WHEN 'TIMESTAMP' THEN 93 WHEN 'CHARACTER' THEN 1 WHEN 'VARCHAR' THEN 12 WHEN 'VARCHAR2' THEN 12 WHEN 'DEC' THEN 3 WHEN 'DECIMAL' THEN 3 WHEN 'BIT' THEN -7 \\n\" +\n            \"WHEN 'INT' THEN 4 WHEN 'INTEGER' THEN 4 WHEN 'BIGINT' THEN -5 WHEN 'BYTE' THEN -6 WHEN 'TINYINT' THEN -6 WHEN 'SMALLINT' THEN 5 WHEN 'BINARY' THEN -2 WHEN 'VARBINARY' THEN -3 WHEN 'FLOAT' THEN 6 WHEN 'DOUBLE' THEN 8\\n\" +\n            \" WHEN 'REAL' THEN 7 WHEN 'DOUBLE PRECISION' THEN 8 WHEN 'DATE' THEN 91 WHEN 'TIME' THEN 92 WHEN 'DATETIME' THEN 93 WHEN 'TEXT' THEN -1 WHEN 'LONGVARCHAR' THEN -1 WHEN 'IMAGE' THEN -4 WHEN 'LONGVARBINARY' THEN -4 \\n\" +\n            \" WHEN 'BLOB' THEN 2004 WHEN 'CLOB' THEN 2005 WHEN 'CURSOR' THEN -10 WHEN 'BOOL' THEN 16 WHEN 'BOOLEAN' THEN 16 ELSE SF_GET_DATA_TYPE(COLS.TYPE$, CAST(COLS.SCALE AS INT), 3) END AS DATA_TYPE,CASE INSTR(COLS.TYPE$,'CLASS',1,1)\\n\" +\n            \"  WHEN 0 THEN COLS.TYPE$ ELSE SF_GET_CLASS_NAME(COLS.TYPE$) END AS TYPE_NAME,CASE SF_GET_COLUMN_SIZE(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT), CAST (COLS.SCALE AS INT)) \\n\" +\n            \"  WHEN -2 THEN NULL ELSE SF_GET_COLUMN_SIZE(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT), CAST (COLS.SCALE AS INT)) END AS COLUMN_SIZE,CASE SF_GET_BUFFER_LEN(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT), CAST (COLS.SCALE AS INT)) \\n\" +\n            \"  WHEN -2 THEN NULL ELSE SF_GET_BUFFER_LEN(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT), CAST (COLS.SCALE AS INT)) END AS BUFFER_LENGTH,CASE SF_GET_DECIMAL_DIGITS(COLS.TYPE$, CAST (COLS.SCALE AS INT)) \\n\" +\n            \"  WHEN -2 THEN NULL ELSE SF_GET_DECIMAL_DIGITS(COLS.TYPE$, CAST (COLS.SCALE AS INT)) END AS DECIMAL_DIGITS,10 AS NUM_PREC_RADIX,CASE COLS.NULLABLE$ WHEN 'Y' THEN 1 ELSE 0 END AS NULLABLE,\\n\" +\n            \"  (SELECT COMMENT$ FROM SYSCOLUMNCOMMENTS WHERE SCHNAME=SCHS.NAME AND TVNAME=TABS.NAME AND COLNAME=COLS.NAME) AS REMARKS,COLS.DEFVAL AS COLUMN_DEF,0 AS SQL_DATA_TYPE,0 AS SQL_DATETIME_SUB,\\n\" +\n            \"  CASE SF_GET_OCT_LENGTH(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT)) WHEN -2 THEN NULL ELSE SF_GET_OCT_LENGTH(COLS.TYPE$, CAST (COLS.LENGTH$ AS INT)) END AS CHAR_OCTET_LENGTH,COLS.COLID + 1 AS ORDINAL_POSITION,CASE COLS.NULLABLE$ \\n\" +\n            \"  WHEN 'Y' THEN 'YES' ELSE 'NO' END AS IS_NULLABLE,NULL AS SCOPE_CATLOG,NULL AS SCOPE_SCHEMA,NULL AS SCOPE_TABLE,0 AS SOURCE_DATA_TYPE FROM\\n\" +\n            \"   (\\n\" +\n            \"   SELECT ID, NAME, PID FROM SYS.SYSOBJECTS WHERE TYPE$ = 'SCH' \\n\" +\n            \"   AND NAME = '%s' \\n\" +\n            \"   )  AS SCHS, (SELECT ID, SCHID, NAME FROM SYS.SYSOBJECTS \\n\" +\n            \"   WHERE TYPE$ = 'SCHOBJ' AND SUBTYPE$ IN('UTAB', 'STAB', 'VIEW')\\n\" +\n            \"     AND NAME LIKE '%s' ESCAPE '!' \\n\" +\n            \"     )  AS TABS, \\n\" +\n            \"SYS.SYSCOLUMNS AS COLS WHERE TABS.ID = COLS.ID AND SCHS.ID = TABS.SCHID  ORDER BY TABLE_SCHEM ASC,TABLE_NAME ASC,ORDINAL_POSITION ASC\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n    @Override\n    public String tableComment() {\n        return \"REMARKS\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"DATA_TYPE\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"REMARKS\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n    @Override\n    public String primaryKeySql(DataSourceConfig dataSourceConfig, String tableName) {\n        return \"  SELECT /*+ MAX_OPT_N_TABLES(5) */ NULL AS TABLE_CAT, 'SYSDBA' AS TABLE_SCHEM,'\" + tableName + \"' AS TABLE_NAME,'true' AS key,\\n\" +\n            \"  COLS.NAME AS COLUMN_NAME,SF_GET_INDEX_KEY_SEQ(INDS.KEYNUM, INDS.KEYINFO, COLS.COLID) AS KEY_SEQ,CONS.NAME AS PK_NAME FROM \\n\" +\n            \"  SYS.SYSINDEXES INDS, (SELECT OBJ.NAME, CON.ID, CON.TYPE$, CON.TABLEID, CON.COLID, CON.INDEXID FROM SYS.SYSCONS AS CON, \\n\" +\n            \"  SYS.SYSOBJECTS AS OBJ WHERE OBJ.SUBTYPE$='CONS' AND OBJ.ID=CON.ID) CONS, SYS.SYSCOLUMNS COLS, (SELECT NAME ,ID FROM SYS.SYSOBJECTS \\n\" +\n            \"  WHERE SUBTYPE$='UTAB' \\n\" +\n            \"  AND NAME = '\" + tableName + \"' \\n\" +\n            \"  AND SCHID=(SELECT ID FROM SYS.SYSOBJECTS WHERE NAME = '\" + dataSourceConfig.getSchemaName() + \"' AND TYPE$='SCH')) TAB, (SELECT ID, NAME FROM SYS.SYSOBJECTS WHERE SUBTYPE$='INDEX')OBJ_INDS \\n\" +\n            \"  WHERE CONS.TYPE$='P' AND CONS.INDEXID=INDS.ID AND INDS.ID=OBJ_INDS.ID AND TAB.ID=COLS.ID AND CONS.TABLEID=TAB.ID AND \\n\" +\n            \"  SF_COL_IS_IDX_KEY(INDS.KEYNUM, INDS.KEYINFO,COLS.COLID)=1 ORDER BY COLUMN_NAME ASC\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/DbQueryDecorator.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.IDbQuery;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.po.LikeTable;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\n/**\n * DbQuery 装饰器\n *\n * @author nieqiurong 2020/9/17.\n * @author hubin\n * @since 3.5.0\n */\npublic class DbQueryDecorator extends AbstractDbQuery {\n    private final IDbQuery dbQuery;\n    @Getter\n    private final Connection connection;\n    private final DbType dbType;\n    private final StrategyConfig strategyConfig;\n    private final String schema;\n    private final Logger logger;\n\n    public DbQueryDecorator(@NotNull DataSourceConfig dataSourceConfig, @NotNull StrategyConfig strategyConfig) {\n        this.dbQuery = dataSourceConfig.getDbQuery();\n        this.connection = dataSourceConfig.getConn();\n        this.dbType = dataSourceConfig.getDbType();\n        this.strategyConfig = strategyConfig;\n        this.schema = dataSourceConfig.getSchemaName();\n        this.logger = LoggerFactory.getLogger(dbQuery.getClass());\n    }\n\n    @Override\n    public String tablesSql() {\n        String tablesSql = dbQuery.tablesSql();\n        if (DbType.POSTGRE_SQL == dbType || DbType.KINGBASE_ES == dbType\n            || DbType.DB2 == dbType || DbType.ORACLE == dbType || DbType.DM == dbType\n            || DbType.GAUSS_DB == dbType) {\n            tablesSql = String.format(tablesSql, this.schema);\n        }\n        if (strategyConfig.isEnableSqlFilter()) {\n            StringBuilder sql = new StringBuilder(tablesSql);\n            LikeTable table;\n            Set<String> tables;\n            if ((table = strategyConfig.getLikeTable()) != null) {\n                sql.append(\" AND \").append(dbQuery.tableName()).append(\" LIKE '\").append(table.getValue()).append(\"'\");\n            } else if ((table = strategyConfig.getNotLikeTable()) != null) {\n                sql.append(\" AND \").append(dbQuery.tableName()).append(\" NOT LIKE '\").append(table.getValue()).append(\"'\");\n            }\n            if (!(tables = strategyConfig.getInclude()).isEmpty()) {\n                sql.append(\" AND \").append(dbQuery.tableName()).append(\" IN (\")\n                    .append(tables.stream().map(tb -> \"'\" + tb + \"'\").collect(Collectors.joining(\",\"))).append(\")\");\n            } else if (!(tables = strategyConfig.getExclude()).isEmpty()) {\n                sql.append(\" AND \").append(dbQuery.tableName()).append(\" NOT IN (\")\n                    .append(tables.stream().map(tb -> \"'\" + tb + \"'\").collect(Collectors.joining(\",\"))).append(\")\");\n            }\n            return sql.toString();\n        }\n        return tablesSql;\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return dbQuery.tableFieldsSql();\n    }\n\n    /**\n     * 扩展{@link #tableFieldsSql()}方法\n     *\n     * @param tableName 表名\n     * @return 查询表字段语句\n     */\n    public String tableFieldsSql(String tableName) {\n        String tableFieldsSql = this.tableFieldsSql();\n        if (DbType.KINGBASE_ES == dbType || DbType.DB2 == dbType) {\n            tableFieldsSql = String.format(tableFieldsSql, this.schema, tableName);\n        } else if (DbType.ORACLE == dbType) {\n            tableFieldsSql = String.format(tableFieldsSql.replace(\"#schema\", this.schema), tableName, tableName.toUpperCase());\n        } else if (DbType.DM == dbType) {\n            tableName = tableName.toUpperCase();\n            tableFieldsSql = String.format(tableFieldsSql, this.schema, tableName);\n        } else if (DbType.POSTGRE_SQL == dbType) {\n            tableFieldsSql = String.format(tableFieldsSql, tableName, tableName, tableName,this.schema);\n        } else if (DbType.GAUSS_DB == dbType) {\n            tableFieldsSql = String.format(tableFieldsSql, this.schema , tableName);\n        } else {\n            tableFieldsSql = String.format(tableFieldsSql, tableName);\n        }\n        return tableFieldsSql;\n    }\n\n    @Override\n    public String tableName() {\n        return dbQuery.tableName();\n    }\n\n    @Override\n    public String tableComment() {\n        return dbQuery.tableComment();\n    }\n\n    @Override\n    public String fieldName() {\n        return dbQuery.fieldName();\n    }\n\n    @Override\n    public String fieldType() {\n        return dbQuery.fieldType();\n    }\n\n    @Override\n    public String fieldComment() {\n        return dbQuery.fieldComment();\n    }\n\n    @Override\n    public String fieldKey() {\n        return dbQuery.fieldKey();\n    }\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) {\n        try {\n            return dbQuery.isKeyIdentity(results);\n        } catch (SQLException e) {\n            logger.warn(\"判断主键自增错误:{}\", e.getMessage());\n            // ignore 这个看到在查H2的时候出了异常，先忽略这个异常了.\n        }\n        return false;\n    }\n\n    @Override\n    public String[] fieldCustom() {\n        return dbQuery.fieldCustom();\n    }\n\n    @Override\n    public String primaryKeySql(DataSourceConfig dataSourceConfig, String tableName) {\n        return dbQuery.primaryKeySql(dataSourceConfig, tableName);\n    }\n\n    public Map<String, Object> getCustomFields(ResultSet resultSet) {\n        String[] fcs = this.fieldCustom();\n        if (null != fcs) {\n            Map<String, Object> customMap = CollectionUtils.newHashMapWithExpectedSize(fcs.length);\n            for (String fc : fcs) {\n                try {\n                    customMap.put(fc, resultSet.getObject(fc));\n                } catch (SQLException sqlException) {\n                    throw new RuntimeException(\"获取自定义字段错误:\", sqlException);\n                }\n            }\n            return customMap;\n        }\n        return Collections.emptyMap();\n    }\n\n    /**\n     * 执行 SQL 查询，回调返回结果\n     *\n     * @param sql      执行SQL\n     * @param consumer 结果处理\n     */\n    public void execute(String sql, Consumer<ResultSetWrapper> consumer) throws SQLException {\n        logger.debug(\"执行SQL:{}\", sql);\n        int count = 0;\n        long start = System.nanoTime();\n        try (PreparedStatement preparedStatement = connection.prepareStatement(sql);\n             ResultSet resultSet = preparedStatement.executeQuery()) {\n            while (resultSet.next()) {\n                consumer.accept(new ResultSetWrapper(resultSet, this, this.dbType));\n                count++;\n            }\n            long end = System.nanoTime();\n            logger.debug(\"返回记录数:{},耗时(ms):{}\", count, (end - start) / 1000000);\n        }\n    }\n\n    public void closeConnection() {\n        Optional.ofNullable(connection).ifPresent((con) -> {\n            try {\n                con.close();\n            } catch (SQLException sqlException) {\n                sqlException.printStackTrace();\n            }\n        });\n    }\n\n    public static class ResultSetWrapper {\n\n        private final IDbQuery dbQuery;\n\n        @Getter\n        private final ResultSet resultSet;\n\n        private final DbType dbType;\n\n        ResultSetWrapper(ResultSet resultSet, IDbQuery dbQuery, DbType dbType) {\n            this.resultSet = resultSet;\n            this.dbQuery = dbQuery;\n            this.dbType = dbType;\n        }\n\n        public String getStringResult(String columnLabel) {\n            try {\n                return resultSet.getString(columnLabel);\n            } catch (SQLException sqlException) {\n                throw new RuntimeException(String.format(\"读取[%s]字段出错!\", columnLabel), sqlException);\n            }\n        }\n\n        /**\n         * @return 获取字段注释\n         */\n        public String getFiledComment() {\n            return getComment(dbQuery.fieldComment());\n        }\n\n        /**\n         * 获取格式化注释\n         *\n         * @param columnLabel 字段列\n         * @return 注释\n         */\n        private String getComment(String columnLabel) {\n            return StringUtils.isNotBlank(columnLabel) ? formatComment(getStringResult(columnLabel)) : StringPool.EMPTY;\n        }\n\n        /**\n         * 获取表注释\n         *\n         * @return 表注释\n         */\n        public String getTableComment() {\n            return getComment(dbQuery.tableComment());\n        }\n\n        /**\n         * @param comment 注释\n         * @return 格式化内容\n         */\n        public String formatComment(String comment) {\n            return StringUtils.isBlank(comment) ? StringPool.EMPTY : comment.replaceAll(\"\\r\\n\", \"\\t\");\n        }\n\n        /**\n         * @return 是否主键\n         */\n        public boolean isPrimaryKey() {\n            String key = this.getStringResult(dbQuery.fieldKey());\n            if (DbType.DB2 == dbType || DbType.SQLITE == dbType || DbType.CLICK_HOUSE == dbType) {\n                return StringUtils.isNotBlank(key) && \"1\".equals(key);\n            } else {\n                return StringUtils.isNotBlank(key) && \"PRI\".equalsIgnoreCase(key);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/DbQueryRegistry.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.IDbQuery;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * 数据库查询接口注册中心\n *\n * @author nieqiuqiu\n * @since 3.3.1\n */\npublic class DbQueryRegistry {\n\n    private final Map<DbType, IDbQuery> db_query_enum_map = new EnumMap<>(DbType.class);\n\n    public DbQueryRegistry() {\n        db_query_enum_map.put(DbType.ORACLE, new OracleQuery());\n        db_query_enum_map.put(DbType.SQL_SERVER, new SqlServerQuery());\n        db_query_enum_map.put(DbType.POSTGRE_SQL, new PostgreSqlQuery());\n        db_query_enum_map.put(DbType.DB2, new DB2Query());\n        db_query_enum_map.put(DbType.MARIADB, new MariadbQuery());\n        db_query_enum_map.put(DbType.H2, new H2Query());\n        db_query_enum_map.put(DbType.LEALONE, new H2Query());\n        db_query_enum_map.put(DbType.SQLITE, new SqliteQuery());\n        db_query_enum_map.put(DbType.DM, new DMQuery());\n        db_query_enum_map.put(DbType.KINGBASE_ES, new KingbaseESQuery());\n        db_query_enum_map.put(DbType.MYSQL, new MySqlQuery());\n        db_query_enum_map.put(DbType.GAUSS, new ZenithQuery());\n        db_query_enum_map.put(DbType.GAUSS_DB, new GaussDBSqlQuery());\n        db_query_enum_map.put(DbType.OSCAR, new OscarQuery());\n        db_query_enum_map.put(DbType.FIREBIRD, new FirebirdQuery());\n        db_query_enum_map.put(DbType.XU_GU, new XuguQuery());\n        db_query_enum_map.put(DbType.CLICK_HOUSE, new ClickHouseQuery());\n        db_query_enum_map.put(DbType.GBASE, new GbaseQuery());\n        db_query_enum_map.put(DbType.SYBASE, new SybaseQuery());\n    }\n\n    public IDbQuery getDbQuery(DbType dbType) {\n        return db_query_enum_map.get(dbType);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/FirebirdQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * MySql 表数据查询\n *\n * @author steven ma\n * @since 2020-08-20\n */\npublic class FirebirdQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"select trim(rdb$relation_name) as rdb$relation_name \" +\n            \"from rdb$relations \" +\n            \"where rdb$view_blr is null \" +\n            \"and (rdb$system_flag is null or rdb$system_flag = 0)\";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"select trim(f.rdb$relation_name) AS rdb$relation_name, \" +\n            \"trim(f.rdb$field_name) AS FIELD, t.rdb$type_name AS  TYPE, \" +\n            \"(CASE WHEN ( \" +\n            \"   SELECT count(*) \" +\n            \"   FROM RDB$RELATION_CONSTRAINTS RC \" +\n            \"   LEFT JOIN RDB$INDICES I ON (I.RDB$INDEX_NAME = RC.RDB$INDEX_NAME) \" +\n            \"   LEFT JOIN RDB$INDEX_SEGMENTS S ON (S.RDB$INDEX_NAME = I.RDB$INDEX_NAME) \" +\n            \"   WHERE (RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY') \" +\n            \"   AND (I.RDB$RELATION_NAME = f.rdb$relation_name  ) \" +\n            \"   AND (S.RDB$FIELD_NAME = f.rdb$field_name) \" +\n            \") > 0 THEN 'PRI' ELSE '' END) AS pk \" +\n            \"from rdb$relation_fields f \" +\n            \"join rdb$relations r on f.rdb$relation_name = r.rdb$relation_name \" +\n            \"JOIN rdb$fields fs ON f.rdb$field_source = fs.rdb$field_name \" +\n            \"JOIN rdb$types  t ON fs.rdb$field_type = t.rdb$type \" +\n            \"and r.rdb$view_blr is NULL \" +\n            \"AND t.rdb$field_name = 'RDB$FIELD_TYPE' \" +\n            \"and (r.rdb$system_flag is null or r.rdb$system_flag = 0) \" +\n            \"AND f.rdb$relation_name = '%s' \" +\n            \"order by 1, f.rdb$field_position\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"rdb$relation_name\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"FIELD\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"TYPE\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"PK\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/GaussDBSqlQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * GaussDB数据库查询\n *\n * @author nieqiurong\n * @since 3.5.13\n */\npublic class GaussDBSqlQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM ( SELECT \\n\" +\n            \"  c.relname AS TABLE_NAME,\\n\" +\n            \"  d.description AS REMARKS\\n\" +\n            \"FROM\\n\" +\n            \"  pg_catalog.pg_namespace n,\\n\" +\n            \"  pg_catalog.pg_class c\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_description d ON (c.OID = d.objoid AND d.objsubid = 0)\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_class dc ON (d.classoid = dc.OID AND dc.relname = 'pg_class')\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_namespace dn ON (dn.OID = dc.relnamespace AND dn.nspname = 'pg_catalog')\\n\" +\n            \"WHERE c.relkind IN ('r', 'p', 'v') AND\\n\" +\n            \"  c.relnamespace = n.OID AND n.nspname = '%s' ) WHERE 1=1 \\n\";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT\\n\" +\n            \"  c.relname,\\n\" +\n            \"  a.attname AS FIELD_NAME,\\n\" +\n            \"  dsc.description AS FIELD_COMMENT,\\n\" +\n//            \"  t.typname AS FIELD_TYPE,\\n\" +\n            \"  format_type(a.atttypid,a.atttypmod) AS FIELD_TYPE,\\n\" +\n            \"  CASE WHEN idx.indisprimary THEN 'PRI' ELSE NULL END AS KEY\\n\" +\n            \"FROM\\n\" +\n            \"  pg_catalog.pg_namespace n\\n\" +\n            \"  JOIN pg_catalog.pg_class c ON (c.relnamespace = n.OID)\\n\" +\n            \"  JOIN pg_catalog.pg_attribute a ON (a.attrelid = c.OID)\\n\" +\n            \"  JOIN pg_catalog.pg_type t ON (a.atttypid = t.OID)\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_attrdef def ON (a.attrelid = def.adrelid AND a.attnum = def.adnum)\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_description dsc ON (c.OID = dsc.objoid AND a.attnum = dsc.objsubid)\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_class dc ON (dc.OID = dsc.classoid AND dc.relname = 'pg_class')\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_namespace dn ON (dc.relnamespace = dn.OID AND dn.nspname = 'pg_catalog')\\n\" +\n            \"  LEFT JOIN pg_catalog.pg_index idx ON idx.indrelid = c.oid AND a.attrelid = idx.indrelid AND a.attnum = ANY(idx.indkey)\\n\" +\n            \"WHERE n.nspname = '%s' AND c.relname = '%s' AND \\n\" +\n            \"  c.relkind IN ('r', 'p', 'v')\\n\" +\n            \"  AND a.attnum > 0\\n\" +\n            \"  AND NOT a.attisdropped\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"REMARKS\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"FIELD_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"FIELD_TYPE\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"FIELD_COMMENT\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/GaussQuery.java",
    "content": "package com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * Gauss 表数据查询\n *\n * @author halower\n * @since 2019-06-27\n * @deprecated 3.5.13 非标准Gauss官方驱动,只适用 jdbc:zenith\n */\n@Deprecated\npublic class GaussQuery extends ZenithQuery {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/GbaseQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n\n/**\n * <a href=\"https://github.com/baomidou/generator/pull/83\">Gbase</a>\n *\n * @author lix\n */\npublic class GbaseQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"select * from systables where 1=1\";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"select a.tabname,b.colname,b.coltype, \" +\n            \"case b.colname \" +\n            \"when 'id' then 'PRI' \" +\n            \"end as key, \" +\n            \"case b.coltype \" +\n            \"when '0' then 'CHAR' \" +\n            \"when '1' then 'SMALLINT' \" +\n            \"when '2' then 'INTEGER' \" +\n            \"when '3' then 'FLOAT' \" +\n            \"when '4' then 'SMALLFLOAT' \" +\n            \"when '5' then 'DECIMAL' \" +\n            \"when '6' then 'SERIAL' \" +\n            \"when '7' then 'DATE' \" +\n            \"when '8' then 'MONEY' \" +\n            \"when '9' then 'NULL' \" +\n            \"when '10' then 'DATETIME' \" +\n            \"when '11' then 'BYTE' \" +\n            \"when '12' then 'TEXT' \" +\n            \"when '13' then 'VARCHAR' \" +\n            \"when '14' then 'INTERVAL' \" +\n            \"when '15' then 'NCHAR' \" +\n            \"when '16' then 'NVARCHAR' \" +\n            \"when '17' then 'INT8' \" +\n            \"when '18' then 'SERIAL8' \" +\n            \"when '19' then 'SET' \" +\n            \"when '20' then 'MULTISET' \" +\n            \"when '21' then 'LIST' \" +\n            \"when '22' then 'Unnamed ROW' \" +\n            \"when '40' then 'LVARCHAR' \" +\n            \"when '41' then 'CLOB' \" +\n            \"when '43' then 'BLOB' \" +\n            \"when '44' then 'BOOLEAN' \" +\n            \"when '256' then 'CHAR' \" +\n            \"when '257' then 'SMALLINT' \" +\n            \"when '258' then 'INTEGER' \" +\n            \"when '259' then 'FLOAT' \" +\n            \"when '260' then 'REAL' \" +\n            \"when '261' then 'DECIMAL' \" +\n            \"when '262' then 'SERIAL' \" +\n            \"when '263' then 'DATE' \" +\n            \"when '264' then 'MONEY' \" +\n            \"when '266' then 'DATETIME' \" +\n            \"when '267' then 'BYTE' \" +\n            \"when '268' then 'TEXT' \" +\n            \"when '269' then 'VARCHAR' \" +\n            \"when '270' then 'INTERVAL' \" +\n            \"when '271' then 'NCHAR' \" +\n            \"when '272' then 'NVARCHAR' \" +\n            \"when '273' then 'INT8' \" +\n            \"when '274' then 'SERIAL8' \" +\n            \"when '275' then 'SET' \" +\n            \"when '276' then 'MULTISET' \" +\n            \"when '277' then 'LIST' \" +\n            \"when '278' then 'Unnamed ROW' \" +\n            \"when '296' then 'LVARCHAR' \" +\n            \"when '297' then 'CLOB' \" +\n            \"when '298' then 'BLOB' \" +\n            \"when '299' then 'BOOLEAN' \" +\n            \"when '4118' then 'Named ROW' \" +\n            \"end as coltypename, b.colno from systables a left join syscolumns b on a.tabid=b.tabid where a.tabid>99 and a.tabtype='T' and a.tabname = 'lap20' order by a.tabname,b.colno;\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"tabname\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"tabname\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"colname\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"coltypename\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"colname\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"key\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/H2Query.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * H2Database 表数据查询 (只适用H2 1.X版本)\n *\n * @author yuxiaobin\n * @since 2019-01-8\n */\npublic class H2Query extends AbstractDbQuery {\n\n    public static final String PK_QUERY_SQL = \"select * from INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME = '%s'\";\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE 1=1 \";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME= '%s' \";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"REMARKS\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"TYPE_NAME\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"REMARKS\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"PRIMARY_KEY\";\n    }\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return results.getString(\"SEQUENCE_NAME\") != null;\n    }\n\n    @Override\n    public String primaryKeySql(DataSourceConfig dataSourceConfig, String tableName) {\n        return String.format(PK_QUERY_SQL, tableName);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/KingbaseESQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * KingbaseES 表数据查询\n *\n * @author kingbase\n * @since 2019-10-12\n */\npublic class KingbaseESQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT A.tablename, obj_description(relfilenode, 'sys_class') AS comments FROM sys_tables A, sys_class B WHERE A.schemaname='%s' AND A.tablename = B.relname\";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT A.attname AS name, format_type(A.atttypid, A.atttypmod) AS type,col_description(A.attrelid, A.attnum) AS comment, (CASE C.contype WHEN 'p' THEN 'PRI' ELSE '' END) AS key \" +\n            \"FROM sys_attribute A LEFT JOIN sys_constraint C ON A.attnum = C.conkey[1] AND A.attrelid = C.conrelid \" +\n            \"WHERE  A.attrelid = '%s.%s'::regclass AND A.attnum > 0 AND NOT A.attisdropped ORDER  BY A.attnum\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"tablename\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"comments\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"name\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"type\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"comment\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"key\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/MariadbQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * MySql 表数据查询\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic class MariadbQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"show table status WHERE 1=1 \";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"show full columns from `%s`\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"NAME\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"COMMENT\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"FIELD\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"TYPE\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENT\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return \"auto_increment\".equals(results.getString(\"Extra\"));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/MySqlQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * MySql 表数据查询\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic class MySqlQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"show table status WHERE 1=1 \";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"show full columns from `%s`\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"NAME\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"COMMENT\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"FIELD\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"TYPE\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENT\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return \"auto_increment\".equals(results.getString(\"Extra\"));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/OracleQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * Oracle 表数据查询\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic class OracleQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM ALL_TAB_COMMENTS WHERE OWNER='%s'\";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT A.COLUMN_NAME, CASE WHEN A.DATA_TYPE='NUMBER' THEN \"\n            + \"(CASE WHEN A.DATA_PRECISION IS NULL THEN A.DATA_TYPE \"\n            + \"WHEN NVL(A.DATA_SCALE, 0) > 0 THEN A.DATA_TYPE||'('||A.DATA_PRECISION||','||A.DATA_SCALE||')' \"\n            + \"ELSE A.DATA_TYPE||'('||A.DATA_PRECISION||')' END) \"\n            + \"ELSE A.DATA_TYPE END DATA_TYPE, B.COMMENTS,DECODE(C.POSITION, '1', 'PRI') KEY \"\n            + \"FROM ALL_TAB_COLUMNS A \"\n            + \" INNER JOIN ALL_COL_COMMENTS B ON A.TABLE_NAME = B.TABLE_NAME AND A.COLUMN_NAME = B.COLUMN_NAME AND B.OWNER = '#schema'\"\n            + \" LEFT JOIN ALL_CONSTRAINTS D ON D.TABLE_NAME = A.TABLE_NAME AND D.CONSTRAINT_TYPE = 'P' AND D.OWNER = '#schema'\"\n            + \" LEFT JOIN ALL_CONS_COLUMNS C ON C.CONSTRAINT_NAME = D.CONSTRAINT_NAME AND C.COLUMN_NAME=A.COLUMN_NAME AND C.OWNER = '#schema'\"\n            + \"WHERE A.OWNER = '#schema' AND (A.TABLE_NAME = '%s' OR A.TABLE_NAME = '%s') ORDER BY A.COLUMN_ID \";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"COMMENTS\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"DATA_TYPE\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENTS\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/OscarQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * <p>Oscar（神通数据库） 表数据查询</p>\n *\n * @author whli\n * @version 1.0.0\n * @since 2020/7/28 18:54\n */\npublic class OscarQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM (SELECT \" +\n            \"a.TABLE_NAME, \" +\n            \"b.COMMENTS \" +\n            \"FROM USER_TABLES a \" +\n            \"INNER JOIN USER_TAB_COMMENTS b ON (b.TABLE_TYPE = 'TABLE' AND a.TABLE_NAME = b.TABLE_NAME)) a WHERE 1=1 \";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT \" +\n            \"T1.COLUMN_NAME, \" +\n            \"T1.DATA_TYPE, \" +\n            \"T2.COMMENTS, \" +\n            \"CASE WHEN T3.CONSTRAINT_TYPE = 'P' THEN 'PRI' \" +\n            \"ELSE '' END KEY \" +\n            \"FROM USER_TAB_COLUMNS T1 \" +\n            \"INNER JOIN USER_COL_COMMENTS T2 ON (T1.COLUMN_NAME = T2.COLUMN_NAME) \" +\n            \"LEFT JOIN(SELECT a.TABLE_NAME,b.COLUMN_NAME,a.CONSTRAINT_TYPE FROM USER_CONSTRAINTS a, USER_IND_COLUMNS b \" +\n            \"WHERE a.CONSTRAINT_TYPE = 'P' AND a.INDEX_NAME = b.INDEX_NAME) T3 ON (T1.TABLE_NAME = T3.TABLE_NAME AND T1.COLUMN_NAME = T3.COLUMN_NAME) \" +\n            \"WHERE T1.TABLE_NAME = '%s' \" +\n            \"GROUP BY T1.COLUMN_NAME,T1.DATA_TYPE,T2.COMMENTS,T3.CONSTRAINT_TYPE \";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"COMMENTS\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"DATA_TYPE\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENTS\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/PostgreSqlQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * PostgreSql 表数据查询\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic class PostgreSqlQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT A.tablename, obj_description(relfilenode, 'pg_class') AS comments FROM pg_tables A, pg_class B WHERE A.schemaname='%s' AND A.tablename = B.relname\";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT\\n\" +\n            \"   A.attname AS name,format_type (A.atttypid,A.atttypmod) AS type,col_description (A.attrelid,A.attnum) AS comment,\\n\" +\n            \"\\t D.column_default,\\n\" +\n            \"   CASE WHEN length(B.attname) > 0 THEN 'PRI' ELSE '' END AS key\\n\" +\n            \"FROM\\n\" +\n            \"   pg_attribute A\\n\" +\n            \"LEFT JOIN (\\n\" +\n            \"    SELECT\\n\" +\n            \"        pg_attribute.attname\\n\" +\n            \"    FROM\\n\" +\n            \"        pg_index,\\n\" +\n            \"        pg_class,\\n\" +\n            \"        pg_attribute\\n\" +\n            \"    WHERE\\n\" +\n            \"        pg_class.oid ='%s' :: regclass\\n\" +\n            \"    AND pg_index.indrelid = pg_class.oid\\n\" +\n            \"    AND pg_attribute.attrelid = pg_class.oid\\n\" +\n            \"    AND pg_attribute.attnum = ANY (pg_index.indkey)\\n\" +\n            \") B ON A.attname = b.attname\\n\" +\n            \"INNER JOIN pg_class C on A.attrelid = C.oid\\n\" +\n            \"INNER JOIN information_schema.columns D on A.attname = D.column_name\\n\" +\n            \"WHERE A.attrelid ='%s' :: regclass AND A.attnum> 0 AND NOT A.attisdropped AND D.table_name = '%s'\\n\" +\n            \" and table_schema='%s'\\n\" +\n            \"ORDER BY A.attnum;\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"tablename\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"comments\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"name\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"type\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"comment\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"key\";\n    }\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return StringUtils.isNotBlank(results.getString(\"column_default\")) && results.getString(\"column_default\").contains(\"nextval\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/SqlServerQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * SqlServer 表数据查询\n *\n * @author hubin\n * @since 2018-01-16\n */\npublic class SqlServerQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"select * from (select cast(so.name as varchar(500)) as TABLE_NAME, \" +\n            \"cast(sep.value as nvarchar(500)) as COMMENTS from sysobjects so \" +\n            \"left JOIN sys.extended_properties sep on sep.major_id=so.id and sep.minor_id=0 \" +\n            \"where (xtype='U' or xtype='v')) a where 1=1 \";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT  cast(a.name AS VARCHAR(500)) AS TABLE_NAME,cast(b.name AS VARCHAR(500)) AS COLUMN_NAME, \"\n            + \"cast(c.VALUE AS NVARCHAR(500)) AS COMMENTS,cast(sys.types.name AS VARCHAR (500)) AS DATA_TYPE,\"\n            + \"(\" + \" SELECT CASE count(1) WHEN 1 then 'PRI' ELSE '' END\"\n            + \" FROM syscolumns,sysobjects,sysindexes,sysindexkeys,systypes \"\n            + \" WHERE syscolumns.xusertype = systypes.xusertype AND syscolumns.id = object_id (a.name) AND sysobjects.xtype = 'PK'\"\n            + \" AND sysobjects.parent_obj = syscolumns.id \" + \" AND sysindexes.id = syscolumns.id \"\n            + \" AND sysobjects.name = sysindexes.name AND sysindexkeys.id = syscolumns.id \"\n            + \" AND sysindexkeys.indid = sysindexes.indid \"\n            + \" AND syscolumns.colid = sysindexkeys.colid AND syscolumns.name = b.name) as 'KEY',\"\n            + \"  b.is_identity isIdentity \"\n            + \" FROM ( select name,object_id from sys.tables UNION all select name,object_id from sys.views ) a \"\n            + \" INNER JOIN sys.columns b ON b.object_id = a.object_id \"\n            + \" LEFT JOIN sys.types ON b.user_type_id = sys.types.user_type_id   \"\n            + \" LEFT JOIN sys.extended_properties c ON c.major_id = b.object_id AND c.minor_id = b.column_id \"\n            + \" WHERE a.name = '%s' and sys.types.name !='sysname' \";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"COMMENTS\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"DATA_TYPE\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENTS\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        return 1 == results.getInt(\"isIdentity\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/SqliteQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * Sqlite 表数据查询\n *\n * @author chen\n * @since 2019-05-08\n */\npublic class SqliteQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"select * from sqlite_master where type='table'\";\n    }\n\n\n    @Override\n    public String tableFieldsSql() {\n        return \"pragma table_info('%s');\";\n    }\n\n\n    @Override\n    public String tableName() {\n        return \"name\";\n    }\n\n\n    @Override\n    public String tableComment() {\n        return \"\";\n    }\n\n\n    @Override\n    public String fieldName() {\n        return \"name\";\n    }\n\n\n    @Override\n    public String fieldType() {\n        return \"type\";\n    }\n\n\n    @Override\n    public String fieldComment() {\n        return \"\";\n    }\n\n\n    @Override\n    public String fieldKey() {\n        return \"pk\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/SybaseQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * Sybase库表信息查询\n *\n * @author lroyia\n * @since 2022/1/19 17:08\n **/\npublic class SybaseQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"select name TABLE_NAME, '' TABLE_COMMENT from sysobjects \";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"select o.name TABLE_NAME, c.name FIELD_NAME, upper(t.name) as FIELD_TYPE, \" +\n            \"(CONVERT(varchar(10),c.id)+ '_' + CONVERT(varchar(10), c.colid)) FIELD_KEY, \" +\n            \"c.length as COL_LENGTH, c.status FIELD_STATUS, '' FIELD_COMMENT, c.colid SORT_INDEX \" +\n            \"FROM syscolumns c left join systypes t \" +\n            \"on c.usertype=t.usertype \" +\n            \"inner join sysobjects o \" +\n            \"on c.id=o.id and o.type='U' \" +\n            \"WHERE o.name = '%s' \" +\n            \"ORDER BY c.colid\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"TABLE_COMMENT\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"FIELD_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"FIELD_TYPE\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"FIELD_COMMENT\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"FIELD_KEY\";\n    }\n\n    @Override\n    public boolean isKeyIdentity(ResultSet results) throws SQLException {\n        // TODO:目前没有找到准确的判断方式，如果有大佬知道，请补充\n        return results.getInt(\"SORT_INDEX\") == 1 && results.getInt(\"FIELD_STATUS\") == 0;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/XuguQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * <p>\n * Xugu 表数据查询\n * </p>\n *\n * @author unique1319 lanjerry\n * @since 2020-10-26\n */\npublic class XuguQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM ALL_TABLES WHERE 1 = 1\";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return \"SELECT B.COL_NAME,B.TYPE_NAME,B.COMMENTS, '' AS KEY FROM ALL_TABLES A INNER JOIN ALL_COLUMNS B ON A.TABLE_ID = B.TABLE_ID WHERE A.TABLE_NAME = '%s'\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"COMMENTS\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"COL_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"TYPE_NAME\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENTS\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/ZenithQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n\n/**\n * Gauss 表数据查询\n *\n * @author halower\n * @since 2019-06-27\n */\npublic class ZenithQuery extends AbstractDbQuery {\n\n    @Override\n    public String tablesSql() {\n        return \"SELECT * FROM (SELECT DISTINCT T1.TABLE_NAME,T2.COMMENTS AS TABLE_COMMENT FROM USER_TAB_COLUMNS T1 \" +\n                \"LEFT JOIN USER_TAB_COMMENTS T2 ON T1.TABLE_NAME = T2.TABLE_NAME ) a WHERE 1=1 \";\n    }\n\n    @Override\n    public String tableFieldsSql() {\n        return\n                \"SELECT T1.COLUMN_NAME,T2.COMMENTS,T1.DATA_TYPE,\" +\n                        \"CASE WHEN CONSTRAINT_TYPE='P' THEN 'PRI' END AS KEY \" +\n                        \"FROM USER_TAB_COLUMNS T1 \" +\n                        \"LEFT JOIN USER_COL_COMMENTS T2 ON \" +\n                        \"(T1.TABLE_NAME = T2.TABLE_NAME AND T1.COLUMN_NAME = T2.COLUMN_NAME) \" +\n                        \"LEFT JOIN (\" +\n                        \"SELECT T4.TABLE_NAME,T4.COLUMN_NAME,T5.CONSTRAINT_TYPE FROM USER_CONS_COLUMNS T4,USER_CONSTRAINTS T5 \" +\n                        \"WHERE T4.CONSTRAINT_NAME = T5.CONSTRAINT_NAME AND T5.CONSTRAINT_TYPE = 'P'\" +\n                        \")T3 ON (T1.TABLE_NAME = T3.TABLE_NAME AND T1.COLUMN_NAME = T3.COLUMN_NAME) \" +\n                        \"WHERE T1.TABLE_NAME = '%s' \" +\n                        \"ORDER BY T1.TABLE_NAME,T1.COLUMN_ID\";\n    }\n\n    @Override\n    public String tableName() {\n        return \"TABLE_NAME\";\n    }\n\n    @Override\n    public String tableComment() {\n        return \"TABLE_COMMENT\";\n    }\n\n    @Override\n    public String fieldName() {\n        return \"COLUMN_NAME\";\n    }\n\n    @Override\n    public String fieldType() {\n        return \"DATA_TYPE\";\n    }\n\n    @Override\n    public String fieldComment() {\n        return \"COMMENTS\";\n    }\n\n    @Override\n    public String fieldKey() {\n        return \"KEY\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/querys/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器，数据库查询相关类\n */\npackage com.baomidou.mybatisplus.generator.config.querys;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/rules/DateType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.rules;\n\n/**\n * 数据库时间类型 到 实体类时间类型 对应策略\n *\n * @author miemie\n * @since 2018/5/22\n */\npublic enum DateType {\n    /**\n     * 只使用 java.util.date 代替\n     */\n    ONLY_DATE,\n    /**\n     * 使用 java.sql 包下的\n     */\n    SQL_PACK,\n    /**\n     * 使用 java.time 包下的\n     * <p>java8 新的时间类型</p>\n     */\n    TIME_PACK\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/rules/DbColumnType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.rules;\n\n/**\n * 表字段类型\n *\n * @author hubin\n * @since 2017-01-11\n */\npublic enum DbColumnType implements IColumnType {\n    // 基本类型\n    BASE_BYTE(\"byte\", null),\n    BASE_SHORT(\"short\", null),\n    BASE_CHAR(\"char\", null),\n    BASE_INT(\"int\", null),\n    BASE_LONG(\"long\", null),\n    BASE_FLOAT(\"float\", null),\n    BASE_DOUBLE(\"double\", null),\n    BASE_BOOLEAN(\"boolean\", null),\n\n    // 包装类型\n    BYTE(\"Byte\", null),\n    SHORT(\"Short\", null),\n    CHARACTER(\"Character\", null),\n    INTEGER(\"Integer\", null),\n    LONG(\"Long\", null),\n    FLOAT(\"Float\", null),\n    DOUBLE(\"Double\", null),\n    BOOLEAN(\"Boolean\", null),\n    STRING(\"String\", null),\n\n    // sql 包下数据类型\n    DATE_SQL(\"Date\", \"java.sql.Date\"),\n    TIME(\"Time\", \"java.sql.Time\"),\n    TIMESTAMP(\"Timestamp\", \"java.sql.Timestamp\"),\n    BLOB(\"Blob\", \"java.sql.Blob\"),\n    CLOB(\"Clob\", \"java.sql.Clob\"),\n\n    // java8 新时间类型\n    LOCAL_DATE(\"LocalDate\", \"java.time.LocalDate\"),\n    LOCAL_TIME(\"LocalTime\", \"java.time.LocalTime\"),\n    YEAR(\"Year\", \"java.time.Year\"),\n    YEAR_MONTH(\"YearMonth\", \"java.time.YearMonth\"),\n    LOCAL_DATE_TIME(\"LocalDateTime\", \"java.time.LocalDateTime\"),\n    INSTANT(\"Instant\", \"java.time.Instant\"),\n\n    // 其他杂类\n    MAP(\"Map\", \"java.util.Map\"),\n    BYTE_ARRAY(\"byte[]\", null),\n    OBJECT(\"Object\", null),\n    DATE(\"Date\", \"java.util.Date\"),\n    BIG_INTEGER(\"BigInteger\", \"java.math.BigInteger\"),\n    BIG_DECIMAL(\"BigDecimal\", \"java.math.BigDecimal\");\n\n    /**\n     * 类型\n     */\n    private final String type;\n\n    /**\n     * 包路径\n     */\n    private final String pkg;\n\n    DbColumnType(final String type, final String pkg) {\n        this.type = type;\n        this.pkg = pkg;\n    }\n\n    @Override\n    public String getType() {\n        return type;\n    }\n\n    @Override\n    public String getPkg() {\n        return pkg;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/rules/IColumnType.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.rules;\n\n/**\n * 获取实体类字段属性类信息接口\n *\n * @author miemie\n * @since 2018-08-22\n */\npublic interface IColumnType {\n\n    /**\n     * 获取字段类型\n     *\n     * @return 字段类型\n     */\n    String getType();\n\n    /**\n     * 获取字段类型完整名\n     *\n     * @return 字段类型完整名\n     */\n    String getPkg();\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/rules/NamingStrategy.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.config.rules;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\n\nimport java.util.Arrays;\nimport java.util.Set;\n\n/**\n * 从数据库表到文件的命名策略\n *\n * @author YangHu, tangguo\n * @since 2016/8/30\n */\npublic enum NamingStrategy {\n\n    /**\n     * 不做任何改变，原样输出\n     */\n    no_change,\n\n    /**\n     * 下划线转驼峰命名\n     */\n    underline_to_camel;\n\n    /**\n     * 下划线转驼峰\n     *\n     * @param name 待转内容\n     */\n    public static String underlineToCamel(String name) {\n        // 快速检查\n        if (StringUtils.isBlank(name)) {\n            // 没必要转换\n            return StringPool.EMPTY;\n        }\n        String tempName = name;\n        // 大写数字下划线组成转为小写 , 允许混合模式转为小写\n        if (StringUtils.isCapitalMode(name) || StringUtils.isMixedMode(name)) {\n            tempName = name.toLowerCase();\n        }\n        StringBuilder result = new StringBuilder();\n        // 用下划线将原始字符串分割\n        String[] camels = tempName.split(ConstVal.UNDERLINE);\n        // 跳过原始字符串中开头、结尾的下换线或双重下划线\n        // 处理真正的驼峰片段\n        Arrays.stream(camels).filter(camel -> !StringUtils.isBlank(camel)).forEach(camel -> {\n            if (result.length() == 0) {\n                // 第一个驼峰片段，首字母都小写\n                result.append(StringUtils.firstToLowerCase(camel));\n            } else {\n                // 其他的驼峰片段，首字母大写\n                result.append(capitalFirst(camel));\n            }\n        });\n        return result.toString();\n    }\n\n    /**\n     * 去掉指定的前缀\n     *\n     * @param name   表名\n     * @param prefix 前缀\n     * @return 转换后的字符串\n     */\n    public static String removePrefix(String name, Set<String> prefix) {\n        if (StringUtils.isBlank(name)) {\n            return StringPool.EMPTY;\n        }\n        // 判断是否有匹配的前缀，然后截取前缀\n        return prefix.stream().filter(pf -> name.toLowerCase().startsWith(pf.toLowerCase()))\n            .findFirst().map(pf -> name.substring(pf.length())).orElse(name);\n    }\n\n    /**\n     * 去掉下划线前缀并转成驼峰格式\n     *\n     * @param name   表名\n     * @param prefix 前缀\n     * @return 转换后的字符串\n     */\n    public static String removePrefixAndCamel(String name, Set<String> prefix) {\n        return underlineToCamel(removePrefix(name, prefix));\n    }\n\n    /**\n     * 去掉指定的后缀\n     *\n     * @param name   表名\n     * @param suffix 后缀\n     * @return 转换后的字符串\n     */\n    public static String removeSuffix(String name, Set<String> suffix) {\n        if (StringUtils.isBlank(name)) {\n            return StringPool.EMPTY;\n        }\n        // 判断是否有匹配的后缀，然后截取后缀\n        return suffix.stream().filter(sf -> name.toLowerCase().endsWith(sf.toLowerCase()))\n            .findFirst().map(sf -> name.substring(0, name.length() - sf.length())).orElse(name);\n    }\n\n    /**\n     * 去掉下划线后缀并转成驼峰格式\n     *\n     * @param name   表名\n     * @param suffix 后缀\n     * @return 转换后的字符串\n     */\n    public static String removeSuffixAndCamel(String name, Set<String> suffix) {\n        return underlineToCamel(removeSuffix(name, suffix));\n    }\n\n    /**\n     * 实体首字母大写\n     *\n     * @param name 待转换的字符串\n     * @return 转换后的字符串\n     */\n    public static String capitalFirst(String name) {\n        if (StringUtils.isNotBlank(name)) {\n            return name.substring(0, 1).toUpperCase() + name.substring(1);\n        }\n        return StringPool.EMPTY;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/config/rules/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器，规则相关类\n */\npackage com.baomidou.mybatisplus.generator.config.rules;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/engine/AbstractTemplateEngine.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.*;\nimport com.baomidou.mybatisplus.generator.config.builder.*;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.util.FileUtils;\nimport com.baomidou.mybatisplus.generator.util.RuntimeUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\n\n\n/**\n * 模板引擎抽象类\n *\n * @author hubin\n * @since 2018-01-10\n */\npublic abstract class AbstractTemplateEngine {\n\n    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());\n\n    /**\n     * 配置信息\n     */\n    private ConfigBuilder configBuilder;\n\n    /**\n     * 模板引擎初始化\n     */\n    @NotNull\n    public abstract AbstractTemplateEngine init(@NotNull ConfigBuilder configBuilder);\n\n    /**\n     * 输出自定义模板文件\n     *\n     * @param customFiles 自定义模板文件列表\n     * @param tableInfo   表信息\n     * @param objectMap   渲染数据\n     * @since 3.5.3\n     */\n    protected void outputCustomFile(@NotNull List<CustomFile> customFiles, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {\n        String entityName = tableInfo.getEntityName();\n        String parentPath = getPathInfo(OutputFile.parent);\n        customFiles.forEach(file -> {\n            String filePath = StringUtils.isNotBlank(file.getFilePath()) ? file.getFilePath() : parentPath;\n            if (StringUtils.isNotBlank(file.getPackageName())) {\n                filePath = filePath + File.separator + file.getPackageName().replaceAll(\"\\\\.\", StringPool.BACK_SLASH + File.separator);\n            }\n            Function<TableInfo, String> formatNameFunction = file.getFormatNameFunction();\n            String fileName = filePath + File.separator + (null != formatNameFunction ? formatNameFunction.apply(tableInfo) : entityName) + file.getFileName();\n            outputFile(new File(fileName), objectMap, file.getTemplatePath(), file.isFileOverride());\n        });\n    }\n\n    /**\n     * 输出实体文件\n     *\n     * @param tableInfo 表信息\n     * @param objectMap 渲染数据\n     * @since 3.5.0\n     */\n    protected void outputEntity(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {\n        String entityName = tableInfo.getEntityName();\n        String entityPath = getPathInfo(OutputFile.entity);\n        Entity entity = this.getConfigBuilder().getStrategyConfig().entity();\n        GlobalConfig globalConfig = configBuilder.getGlobalConfig();\n        if (entity.isGenerate()) {\n            String entityFile = String.format((entityPath + File.separator + \"%s\" + suffixJavaOrKt()), entityName);\n            outputFile(getOutputFile(entityFile, OutputFile.entity), objectMap, templateFilePath(globalConfig.isKotlin() ? entity.getKotlinTemplate() : entity.getJavaTemplate()), getConfigBuilder().getStrategyConfig().entity().isFileOverride());\n        }\n    }\n\n    protected File getOutputFile(String filePath, OutputFile outputFile) {\n        return getConfigBuilder().getStrategyConfig().getOutputFile().createFile(filePath, outputFile);\n    }\n\n    /**\n     * 输出Mapper文件(含xml)\n     *\n     * @param tableInfo 表信息\n     * @param objectMap 渲染数据\n     * @since 3.5.0\n     */\n    protected void outputMapper(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {\n        // MpMapper.java\n        String entityName = tableInfo.getEntityName();\n        String mapperPath = getPathInfo(OutputFile.mapper);\n        Mapper mapper = this.getConfigBuilder().getStrategyConfig().mapper();\n        if (mapper.isGenerateMapper()) {\n            String mapperFile = String.format((mapperPath + File.separator + tableInfo.getMapperName() + suffixJavaOrKt()), entityName);\n            outputFile(getOutputFile(mapperFile, OutputFile.mapper), objectMap, templateFilePath(mapper.getMapperTemplatePath()), getConfigBuilder().getStrategyConfig().mapper().isFileOverride());\n        }\n        // MpMapper.xml\n        String xmlPath = getPathInfo(OutputFile.xml);\n        if (mapper.isGenerateMapperXml()) {\n            String xmlFile = String.format((xmlPath + File.separator + tableInfo.getXmlName() + ConstVal.XML_SUFFIX), entityName);\n            outputFile(getOutputFile(xmlFile, OutputFile.xml), objectMap, templateFilePath(mapper.getMapperXmlTemplatePath()), getConfigBuilder().getStrategyConfig().mapper().isFileOverride());\n        }\n    }\n\n    /**\n     * 输出service文件\n     *\n     * @param tableInfo 表信息\n     * @param objectMap 渲染数据\n     * @since 3.5.0\n     */\n    protected void outputService(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {\n        // IMpService.java\n        String entityName = tableInfo.getEntityName();\n        // 判断是否要生成service接口\n        Service service = this.getConfigBuilder().getStrategyConfig().service();\n        if (service.isGenerateService()) {\n            String servicePath = getPathInfo(OutputFile.service);\n            String serviceFile = String.format((servicePath + File.separator + tableInfo.getServiceName() + suffixJavaOrKt()), entityName);\n            outputFile(getOutputFile(serviceFile, OutputFile.service), objectMap, templateFilePath(service.getServiceTemplate()), getConfigBuilder().getStrategyConfig().service().isFileOverride());\n        }\n        // MpServiceImpl.java\n        String serviceImplPath = getPathInfo(OutputFile.serviceImpl);\n        if (service.isGenerateServiceImpl()) {\n            String implFile = String.format((serviceImplPath + File.separator + tableInfo.getServiceImplName() + suffixJavaOrKt()), entityName);\n            outputFile(getOutputFile(implFile, OutputFile.serviceImpl), objectMap, templateFilePath(service.getServiceImplTemplate()), getConfigBuilder().getStrategyConfig().service().isFileOverride());\n        }\n    }\n\n    /**\n     * 输出controller文件\n     *\n     * @param tableInfo 表信息\n     * @param objectMap 渲染数据\n     * @since 3.5.0\n     */\n    protected void outputController(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {\n        // MpController.java\n        Controller controller = this.getConfigBuilder().getStrategyConfig().controller();\n        String controllerPath = getPathInfo(OutputFile.controller);\n        if (controller.isGenerate()) {\n            String entityName = tableInfo.getEntityName();\n            String controllerFile = String.format((controllerPath + File.separator + tableInfo.getControllerName() + suffixJavaOrKt()), entityName);\n            outputFile(getOutputFile(controllerFile, OutputFile.controller), objectMap, templateFilePath(controller.getTemplatePath()), getConfigBuilder().getStrategyConfig().controller().isFileOverride());\n        }\n    }\n\n    /**\n     * 输出文件\n     *\n     * @param file         文件\n     * @param objectMap    渲染信息\n     * @param templatePath 模板路径\n     * @param fileOverride 是否覆盖已有文件\n     * @since 3.5.2\n     */\n    protected void outputFile(@NotNull File file, @NotNull Map<String, Object> objectMap, @NotNull String templatePath, boolean fileOverride) {\n        if (isCreate(file, fileOverride)) {\n            try {\n                // 全局判断【默认】\n                boolean exist = file.exists();\n                if (!exist) {\n                    File parentFile = file.getParentFile();\n                    FileUtils.forceMkdir(parentFile);\n                }\n                LOGGER.debug(\"templatePath:{};  file:{}\", templatePath, file);\n                writer(objectMap, templatePath, file);\n            } catch (Exception exception) {\n                throw new RuntimeException(exception);\n            }\n        }\n    }\n\n    /**\n     * 获取模板路径\n     *\n     * @param function function\n     * @return 模板路径\n     * @since 3.5.0\n     * @deprecated 3.5.6\n     */\n    @NotNull\n    @Deprecated\n    protected Optional<String> getTemplateFilePath(@NotNull Function<TemplateConfig, String> function) {\n        TemplateConfig templateConfig = getConfigBuilder().getTemplateConfig();\n        String filePath = function.apply(templateConfig);\n        if (StringUtils.isNotBlank(filePath)) {\n            return Optional.of(templateFilePath(filePath));\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * 获取路径信息\n     *\n     * @param outputFile 输出文件\n     * @return 路径信息\n     */\n    @Nullable\n    protected String getPathInfo(@NotNull OutputFile outputFile) {\n        return getConfigBuilder().getPathInfo().get(outputFile);\n    }\n\n    /**\n     * 批量输出 java xml 文件\n     */\n    @NotNull\n    public AbstractTemplateEngine batchOutput() {\n        try {\n            ConfigBuilder config = this.getConfigBuilder();\n            List<TableInfo> tableInfoList = config.getTableInfoList();\n            tableInfoList.forEach(tableInfo -> {\n                Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);\n                Optional.ofNullable(config.getInjectionConfig()).ifPresent(t -> {\n                    // 添加自定义属性\n                    t.beforeOutputFile(tableInfo, objectMap);\n                    // 输出自定义文件\n                    outputCustomFile(t.getCustomFiles(), tableInfo, objectMap);\n                });\n                // entity\n                outputEntity(tableInfo, objectMap);\n                // mapper and xml\n                outputMapper(tableInfo, objectMap);\n                // service\n                outputService(tableInfo, objectMap);\n                // controller\n                outputController(tableInfo, objectMap);\n            });\n        } catch (Exception e) {\n            throw new RuntimeException(\"An exception occurred in the output file: \", e);\n        }\n        return this;\n    }\n\n    /**\n     * 将模板转化成为字符串\n     *\n     * @param objectMap      渲染对象 MAP 信息\n     * @param templateName   模板名称\n     * @param templateString 模板字符串\n     * @since 3.5.0\n     */\n    public abstract String writer(@NotNull Map<String, Object> objectMap, @NotNull String templateName, @NotNull String templateString) throws Exception;\n\n    /**\n     * 将模板转化成为文件\n     *\n     * @param objectMap    渲染对象 MAP 信息\n     * @param templatePath 模板文件\n     * @param outputFile   文件生成的目录\n     * @throws Exception 异常\n     * @since 3.5.0\n     */\n    public abstract void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception;\n\n    /**\n     * 打开输出目录\n     */\n    public void open() {\n        String outDir = getConfigBuilder().getGlobalConfig().getOutputDir();\n        if(StringUtils.isBlank(outDir)){\n            LOGGER.warn(\"The output directory is not configured\");\n            return;\n        }\n        if(!new File(outDir).exists()){\n            LOGGER.warn(\"The output directory [{}] does not exist\", outDir);\n            return;\n        }\n        if (getConfigBuilder().getGlobalConfig().isOpen()) {\n            try {\n                RuntimeUtils.openDir(outDir);\n            } catch (IOException e) {\n                LOGGER.error(e.getMessage(), e);\n            }\n        }\n    }\n\n    /**\n     * 渲染对象 MAP 信息\n     *\n     * @param config    配置信息\n     * @param tableInfo 表信息对象\n     * @return ignore\n     */\n    @NotNull\n    public Map<String, Object> getObjectMap(@NotNull ConfigBuilder config, @NotNull TableInfo tableInfo) {\n        Map<String, Object> objectMap = new HashMap<>();\n        StrategyConfig strategyConfig = config.getStrategyConfig();\n        // 启用 schema 处理逻辑\n        String schemaName = \"\";\n        if (strategyConfig.isEnableSchema()) {\n            // 存在 schemaName 设置拼接 . 组合表名\n            schemaName = config.getDataSourceConfig().getSchemaName();\n            if (StringUtils.isNotBlank(schemaName)) {\n                tableInfo.setSchemaName(schemaName);\n                schemaName += \".\";\n                tableInfo.setConvert(true);\n            }\n        }\n        objectMap.put(\"schemaName\", schemaName);\n        Map<String, Object> controllerData = strategyConfig.controller().renderData(tableInfo);\n        objectMap.putAll(controllerData);\n        Map<String, Object> mapperData = strategyConfig.mapper().renderData(tableInfo);\n        objectMap.putAll(mapperData);\n        Map<String, Object> serviceData = strategyConfig.service().renderData(tableInfo);\n        objectMap.putAll(serviceData);\n        Map<String, Object> entityData = strategyConfig.entity().renderData(tableInfo);\n        objectMap.putAll(entityData);\n        objectMap.put(\"config\", config);\n        objectMap.put(\"package\", config.getPackageConfig().getPackageInfo(config.getInjectionConfig()));\n        GlobalConfig globalConfig = config.getGlobalConfig();\n        objectMap.put(\"author\", globalConfig.getAuthor());\n        objectMap.put(\"kotlin\", globalConfig.isKotlin());\n        objectMap.put(\"swagger\", globalConfig.isSwagger());\n        objectMap.put(\"springdoc\", globalConfig.isSpringdoc());\n        objectMap.put(\"date\", globalConfig.getCommentDate());\n        objectMap.put(\"table\", tableInfo);\n        objectMap.put(\"entity\", tableInfo.getEntityName());\n        return objectMap;\n    }\n\n    /**\n     * 模板真实文件路径\n     *\n     * @param filePath 文件路径\n     * @return ignore\n     */\n    @NotNull\n    public abstract String templateFilePath(@NotNull String filePath);\n\n    /**\n     * 检查文件是否创建文件\n     *\n     * @param file         文件\n     * @param fileOverride 是否覆盖已有文件\n     * @return 是否创建文件\n     * @since 3.5.2\n     */\n    protected boolean isCreate(@NotNull File file, boolean fileOverride) {\n        if (file.exists() && !fileOverride) {\n            LOGGER.warn(\"File [{}] already exists. Overwrite mode is disabled. Enable this feature in the policy settings\", file.getName());\n        }\n        return !file.exists() || fileOverride;\n    }\n\n    /**\n     * 文件后缀\n     */\n    protected String suffixJavaOrKt() {\n        return getConfigBuilder().getGlobalConfig().isKotlin() ? ConstVal.KT_SUFFIX : ConstVal.JAVA_SUFFIX;\n    }\n\n    @NotNull\n    public ConfigBuilder getConfigBuilder() {\n        return configBuilder;\n    }\n\n    @NotNull\n    public AbstractTemplateEngine setConfigBuilder(@NotNull ConfigBuilder configBuilder) {\n        this.configBuilder = configBuilder;\n        return this;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/engine/BeetlTemplateEngine.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport org.beetl.core.Configuration;\nimport org.beetl.core.GroupTemplate;\nimport org.beetl.core.Template;\nimport org.beetl.core.resource.ClasspathResourceLoader;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.Map;\n\n/**\n * Beetl 模板引擎实现文件输出\n *\n * @author yandixuan\n * @since 2018-12-16\n */\npublic class BeetlTemplateEngine extends AbstractTemplateEngine {\n\n    private static Method method;\n\n    static {\n        try {\n            method = GroupTemplate.class.getDeclaredMethod(\"getTemplate\", Object.class);\n        } catch (NoSuchMethodException e) {\n            try {\n                //3.2.x 方法签名修改成了object,其他低版本为string\n                method = GroupTemplate.class.getDeclaredMethod(\"getTemplate\", String.class);\n            } catch (NoSuchMethodException exception) {\n                throw new RuntimeException(exception);\n            }\n        }\n    }\n\n    private GroupTemplate groupTemplate;\n\n    @Override\n    public @NotNull AbstractTemplateEngine init(@NotNull ConfigBuilder configBuilder) {\n        try {\n            Configuration cfg = Configuration.defaultConfiguration();\n            groupTemplate = new GroupTemplate(new ClasspathResourceLoader(\"/\"), cfg);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        return this;\n    }\n\n    @Override\n    public String writer(@NotNull Map<String, Object> objectMap, @NotNull String templateName, @NotNull String templateString) throws Exception {\n        Template template = groupTemplate.getTemplate(templateString);\n        template.binding(objectMap);\n        return template.render();\n    }\n\n    @Override\n    public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {\n        Template template = (Template) method.invoke(groupTemplate, templatePath);\n        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {\n            template.binding(objectMap);\n            template.renderTo(fileOutputStream);\n        }\n        LOGGER.debug(\"模板:{};  文件:{}\", templatePath, outputFile);\n    }\n\n    @Override\n    public @NotNull String templateFilePath(@NotNull String filePath) {\n        return filePath + \".btl\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/engine/EnjoyTemplateEngine.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.jfinal.template.Engine;\nimport com.jfinal.template.Template;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.OutputStreamWriter;\nimport java.util.Map;\n\n/**\n * enjoy 模板引擎实现文件输出\n *\n * @author flyinke\n * @since 2022-06-16\n */\npublic class EnjoyTemplateEngine extends AbstractTemplateEngine {\n\n    private Engine engine;\n\n    @Override\n    public @NotNull AbstractTemplateEngine init(@NotNull ConfigBuilder configBuilder) {\n        engine = Engine.createIfAbsent(\"mybatis-plus-generator\",\n            Engine::setToClassPathSourceFactory);\n        return this;\n    }\n\n    @Override\n    public String writer(@NotNull Map<String, Object> objectMap, @NotNull String templateName, @NotNull String templateString) throws Exception {\n        Template template = engine.getTemplateByString(templateString);\n        return template.renderToString(objectMap);\n    }\n\n    @Override\n    public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {\n        String str = engine.getTemplate(templatePath).renderToString(objectMap);\n        try (FileOutputStream fos = new FileOutputStream(outputFile);\n             OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);\n             BufferedWriter writer = new BufferedWriter(ow)) {\n            writer.append(str);\n        }\n    }\n\n    @Override\n    public @NotNull String templateFilePath(@NotNull String filePath) {\n        final String dotVm = \".ej\";\n        return filePath.endsWith(dotVm) ? filePath : filePath + dotVm;\n    }\n}\n\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/engine/FreemarkerTemplateEngine.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport freemarker.template.Configuration;\nimport freemarker.template.Template;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.StringWriter;\nimport java.util.Map;\n\n/**\n * Freemarker 模板引擎实现文件输出\n *\n * @author nieqiurong\n * @since 2018-01-11\n */\npublic class FreemarkerTemplateEngine extends AbstractTemplateEngine {\n    private Configuration configuration;\n\n    @Override\n    public @NotNull FreemarkerTemplateEngine init(@NotNull ConfigBuilder configBuilder) {\n        configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);\n        configuration.setDefaultEncoding(ConstVal.UTF8);\n        configuration.setClassForTemplateLoading(FreemarkerTemplateEngine.class, StringPool.SLASH);\n        return this;\n    }\n\n    @Override\n    public String writer(@NotNull Map<String, Object> objectMap, @NotNull String templateName, @NotNull String templateString) throws Exception {\n        Template template = new Template(templateName, templateString, configuration);\n        StringWriter writer = new StringWriter();\n        template.process(objectMap, writer);\n        return writer.toString();\n    }\n\n    @Override\n    public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {\n        Template template = configuration.getTemplate(templatePath);\n        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {\n            template.process(objectMap, new OutputStreamWriter(fileOutputStream, ConstVal.UTF8));\n        }\n    }\n\n\n    @Override\n    public @NotNull String templateFilePath(@NotNull String filePath) {\n        return filePath + \".ftl\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/engine/VelocityTemplateEngine.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\nimport com.baomidou.mybatisplus.generator.config.TemplateLoadWay;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport org.apache.velocity.Template;\nimport org.apache.velocity.VelocityContext;\nimport org.apache.velocity.app.Velocity;\nimport org.apache.velocity.app.VelocityEngine;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.*;\nimport java.util.Map;\nimport java.util.Properties;\n\n/**\n * Velocity 模板引擎实现文件输出\n *\n * @author hubin\n * @since 2018-01-10\n */\npublic class VelocityTemplateEngine extends AbstractTemplateEngine {\n    private VelocityEngine velocityEngine;\n\n    {\n        try {\n            Class.forName(\"org.apache.velocity.util.DuckType\");\n        } catch (ClassNotFoundException e) {\n            // velocity1.x的生成格式错乱 https://github.com/baomidou/generator/issues/5\n            LOGGER.warn(\"Velocity 1.x is outdated, please upgrade to 2.x or later.\");\n        }\n    }\n\n    @Override\n    public @NotNull VelocityTemplateEngine init(@NotNull ConfigBuilder configBuilder) {\n        if (null == velocityEngine) {\n            Properties p = new Properties();\n            p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);\n            p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);\n            if (configBuilder.getTemplateLoadWay().isFile()) {\n                // 文件模板\n                p.setProperty(ConstVal.VM_LOAD_PATH_KEY, ConstVal.VM_LOAD_PATH_VALUE);\n                p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY);\n                p.setProperty(\"file.resource.loader.unicode\", StringPool.TRUE);\n            } else {\n                // 文本模板\n                p.setProperty(Velocity.RESOURCE_LOADER, TemplateLoadWay.STRING.getValue());\n            }\n            velocityEngine = new VelocityEngine(p);\n        }\n        return this;\n    }\n\n    @Override\n    public String writer(@NotNull Map<String, Object> objectMap, @NotNull String templateName, @NotNull String templateString) throws Exception {\n        StringWriter writer = new StringWriter();\n        velocityEngine.evaluate(new VelocityContext(objectMap), writer, templateName, templateString);\n        return writer.toString();\n    }\n\n    @Override\n    public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {\n        Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);\n        try (FileOutputStream fos = new FileOutputStream(outputFile);\n             OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);\n             BufferedWriter writer = new BufferedWriter(ow)) {\n            template.merge(new VelocityContext(objectMap), writer);\n        }\n    }\n\n\n    @Override\n    public @NotNull String templateFilePath(@NotNull String filePath) {\n        final String dotVm = \".vm\";\n        return filePath.endsWith(dotVm) ? filePath : filePath + dotVm;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/fill/Column.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.fill;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.generator.IFill;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 字段填充\n *\n * @author nieqiurong\n * @since 3.5.0 2020/12/1.\n */\npublic class Column implements IFill {\n\n    private final String columnName;\n\n    private final FieldFill fieldFill;\n\n    public Column(@NotNull String columnName, @NotNull FieldFill fieldFill) {\n        this.columnName = columnName;\n        this.fieldFill = fieldFill;\n    }\n\n    public Column(String columnName) {\n        this.columnName = columnName;\n        this.fieldFill = FieldFill.DEFAULT;\n    }\n\n    @Override\n    public @NotNull String getName() {\n        return this.columnName;\n    }\n\n    @Override\n    public @NotNull FieldFill getFieldFill() {\n        return this.fieldFill;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/fill/Property.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.fill;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.generator.IFill;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 属性填充\n *\n * @author nieqiurong\n * @since 3.5.0 2020/11/30.\n */\npublic class Property implements IFill {\n\n    private final String propertyName;\n\n    private final FieldFill fieldFill;\n\n    public Property(@NotNull String propertyName, @NotNull FieldFill fieldFill) {\n        this.propertyName = propertyName;\n        this.fieldFill = fieldFill;\n    }\n\n    public Property(@NotNull String propertyName) {\n        this.propertyName = propertyName;\n        this.fieldFill = FieldFill.DEFAULT;\n    }\n\n    @Override\n    public @NotNull String getName() {\n        return this.propertyName;\n    }\n\n    @Override\n    public @NotNull FieldFill getFieldFill() {\n        return this.fieldFill;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/function/ConverterFileName.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.function;\n\n\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 转换输出文件名称\n *\n * @author nieqiurong 2020/11/05.\n * @since 3.5.0\n */\n@FunctionalInterface\npublic interface ConverterFileName {\n\n    @NotNull\n    String convert(String entityName);\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/index/AbstractMapperMethodHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.index;\n\nimport com.baomidou.mybatisplus.generator.IGenerateMapperMethodHandler;\nimport com.baomidou.mybatisplus.generator.config.ConstVal;\n\n/**\n * @author nieqiurong\n * @since 3.5.10\n */\npublic abstract class AbstractMapperMethodHandler implements IGenerateMapperMethodHandler {\n\n    /**\n     * 生成Java方法(default)\n     * <pre>\n     * default ${returnValue} ${methodName}(${args}) {\n     *    return ${returnBody};\n     * }\n     * <pre/>\n     * Example:\n     * <pre>\n     * default UserInfo selectByCardNo(String cardNo) {\n     *    return selectOne(Wrappers.<UserInfo>query().eq(TUserInfo.CARD_NO, cardNo));\n     * }\n     * </pre>\n     */\n    public String buildMethod(String methodName, String args, String returnValue, String returnBody) {\n        return \"default\" + \" \" +\n            returnValue + \" \" + methodName + \"(\" + args + \")\" + \" \" + \"{\" + \"\\n\" +\n            \"        return \" + returnBody + \";\" + \"\\n\" +\n            \"    }\\n\";\n    }\n\n    /**\n     * 构建Kotlin方法\n     * <pre>\n     * fun ${methodName}(${args}) :${returnValue} {\n     *    return ${returnBody};\n     * }\n     * </pre>\n     * Example:\n     * <pre>\n     * fun selectByCardNo(cardNo: String) :UserInfo? {\n     *    return selectOne(Wrappers.query<UserInfo>().eq(UserInfo.CARD_NO, cardNo));\n     * }\n     * </pre>\n     *\n     * @param methodName  方法名\n     * @param args        参数列表\n     * @param returnValue 返回值\n     * @param returnBody  返回体\n     * @return 方法\n     */\n    public String buildKotlinMethod(String methodName, String args, String returnValue, String returnBody) {\n        return \"fun \" + methodName + \"(\" + args + \")\" + \" :\" + returnValue + \" {\" + \"\\n\" +\n            \"        return \" + returnBody + \";\" + \"\\n\" +\n            \"    }\\n\";\n    }\n\n    /**\n     * 判断当前索引名称是否为主键索引 (索引名为PRIMARY或PRIMARY开头的为主键索引)\n     *\n     * @param indexName 索引名称\n     * @return 是否为主键索引\n     * @since 3.5.12\n     */\n    public boolean isPrimaryKey(String indexName) {\n        return indexName.toUpperCase().startsWith(ConstVal.PRIMARY);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/index/DefaultGenerateMapperLambdaMethodHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.index;\n\nimport com.baomidou.mybatisplus.core.toolkit.ObjectUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper;\nimport com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport com.baomidou.mybatisplus.generator.model.MapperMethod;\nimport com.baomidou.mybatisplus.generator.util.KotlinTypeUtils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 使用Lambda方式生成索引方法\n * <p>复合索引下,第一个字段不会判空,后续字段会进行判空处理,也就是只能保证第一个字段不传递空,无法解决掉索引中间项传递为空的情况</p>\n * <p>由于需求不一样,默认只处理单字段索引,如果默认复合索引的方案符合你的要求,你可以考虑{@link #singleIndex}设置成false</p>\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic class DefaultGenerateMapperLambdaMethodHandler extends AbstractMapperMethodHandler {\n\n    /**\n     * 只生成单索引字段方法(默认true)\n     * <p>当设置为true时,代表会过滤掉复合索引</p>\n     */\n    private final boolean singleIndex;\n\n    public DefaultGenerateMapperLambdaMethodHandler() {\n        this(true);\n    }\n\n    public DefaultGenerateMapperLambdaMethodHandler(boolean singleIndex) {\n        this.singleIndex = singleIndex;\n    }\n\n    @Override\n    public List<MapperMethod> getMethodList(TableInfo tableInfo) {\n        Map<String, List<DatabaseMetaDataWrapper.Index>> indexlistMap = tableInfo.getIndexList().stream()\n            .collect(Collectors.groupingBy(DatabaseMetaDataWrapper.Index::getName));\n        String entityName = tableInfo.getEntityName();\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        StrategyConfig strategyConfig = tableInfo.getStrategyConfig();\n        Entity entity = strategyConfig.entity();\n        Set<Map.Entry<String, List<DatabaseMetaDataWrapper.Index>>> entrySet = indexlistMap.entrySet();\n        List<MapperMethod> methodList = new ArrayList<>();\n        for (Map.Entry<String, List<DatabaseMetaDataWrapper.Index>> entry : entrySet) {\n            String indexName = entry.getKey();\n            List<DatabaseMetaDataWrapper.Index> indexList = entry.getValue();\n            int indexSize = indexList.size();\n            if (this.singleIndex && indexSize > 1) {\n                continue;\n            }\n            // use byId\n            if (indexSize == 1 && isPrimaryKey(indexName)) {\n                continue;\n            }\n            Map<String, TableField> tableFieldMap = tableInfo.getTableFieldMap();\n            StringBuilder baseMethodNameBuilder = new StringBuilder();\n            StringBuilder argsBuilder = new StringBuilder();\n            StringBuilder baseWrapperBuilder = new StringBuilder();\n            boolean uniqueKey = false;\n            List<TableField> tableFieldList = new ArrayList<>();\n            for (int i = 0; i < indexSize; i++) {\n                DatabaseMetaDataWrapper.Index index = indexList.get(i);\n                if (index.isUnique()) {\n                    uniqueKey = true;\n                }\n                TableField tableField = tableFieldMap.get(index.getColumnName());\n                if (index.getColumnName().equals(entity.getLogicDeleteColumnName())\n                    || tableField.getPropertyName().equals(entity.getLogicDeletePropertyName())) {\n                    continue;\n                }\n                tableFieldList.add(tableField);\n                baseMethodNameBuilder.append(tableField.getCapitalName());\n                if (indexSize > 1) {\n                    baseWrapperBuilder.append(\"eq(ObjectUtils.isNotNull(\").append(tableField.getPropertyName()).append(\")\").append(\", \").append(entityName).append(\"::\");\n                } else {\n                    baseWrapperBuilder.append(\"eq(\").append(entityName).append(\"::\");\n                }\n                if (globalConfig.isKotlin()) {\n                    baseWrapperBuilder.append(tableField.getPropertyName()).append(\",\").append(\" \").append(tableField.getPropertyName()).append(\")\");\n                    argsBuilder.append(tableField.getPropertyName()).append(\":\").append(\" \")\n                        .append(KotlinTypeUtils.getStringType(tableField.getColumnType()));\n                    if (i > 0) {\n                        argsBuilder.append(\"?\");\n                    }\n                } else {\n                    if (\"boolean\".equals(tableField.getPropertyType())) {\n                        baseWrapperBuilder.append(\"is\").append(tableField.getCapitalName());\n                    } else {\n                        baseWrapperBuilder.append(\"get\").append(tableField.getCapitalName());\n                    }\n                    baseWrapperBuilder.append(\",\").append(\" \").append(tableField.getPropertyName()).append(\")\");\n                    argsBuilder.append(tableField.getColumnType().getType()).append(\" \").append(tableField.getPropertyName());\n                }\n                if (i < indexSize - 1) {\n                    baseWrapperBuilder.append(\".\");\n                    baseMethodNameBuilder.append(\"And\");\n                    argsBuilder.append(\", \");\n                }\n            }\n            String baseMethodName = baseMethodNameBuilder.toString();\n            if (StringUtils.isBlank(baseMethodName)) {\n                continue;\n            }\n            boolean returnList = (indexSize > 1 || !uniqueKey);\n            String args = argsBuilder.toString();\n            String baseWrapper = baseWrapperBuilder.toString();\n            if (globalConfig.isKotlin()) {\n                String selectByMethod;\n                if (returnList) {\n                    selectByMethod = buildKotlinMethod(\"selectBy\" + baseMethodName, args, \"List<\" + tableInfo.getEntityName() + \">?\",\n                        \"selectList(KtQueryWrapper(\" + tableInfo.getEntityName() + \"::class.java).\" + baseWrapper + \")\");\n                } else {\n                    selectByMethod = buildKotlinMethod(\"selectBy\" + baseMethodName, args, tableInfo.getEntityName() + \"?\",\n                        \"selectOne(KtQueryWrapper(\" + tableInfo.getEntityName() + \"::class.java).\" + baseWrapper + \")\");\n                }\n                String updateByMethod = buildKotlinMethod(\"updateBy\" + baseMethodName, \"entity:\" + \" \" + tableInfo.getEntityName() + \", \" + args, \"Int\",\n                    \"update(entity, KtUpdateWrapper(\" + tableInfo.getEntityName() + \"::class.java).\" + baseWrapper + \")\");\n                String deleteByMethod = buildKotlinMethod(\"deleteBy\" + baseMethodName, args, \"Int\",\n                    \"delete(KtUpdateWrapper(\" + tableInfo.getEntityName() + \"::class.java).\" + baseWrapper + \")\");\n                methodList.add(new MapperMethod(indexName, selectByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, updateByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, deleteByMethod, tableFieldList));\n            } else {\n                String selectByMethod;\n                if (returnList) {\n                    selectByMethod = buildMethod(\"selectBy\" + baseMethodName, args, \"List<\" + entityName + \">\",\n                        \"selectList(Wrappers.<\" + tableInfo.getEntityName() + \">lambdaQuery().\" + baseWrapper + \")\");\n                } else {\n                    selectByMethod = buildMethod(\"selectBy\" + baseMethodName, args, entityName,\n                        \"selectOne(Wrappers.<\" + tableInfo.getEntityName() + \">lambdaQuery().\" + baseWrapper + \")\");\n                }\n                String updateByMethod = buildMethod(\n                    \"updateBy\" + baseMethodName, tableInfo.getEntityName() + \" entity\" + \", \" + args,\n                    \"int\", \"update(entity, Wrappers.<\" + tableInfo.getEntityName() + \">lambdaUpdate().\" + baseWrapper + \")\");\n                String deleteByMethod = buildMethod(\"deleteBy\" + baseMethodName, args, \"int\",\n                    \"delete(Wrappers.<\" + tableInfo.getEntityName() + \">lambdaUpdate().\" + baseWrapper + \")\");\n                methodList.add(new MapperMethod(indexName, selectByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, updateByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, deleteByMethod, tableFieldList));\n            }\n        }\n        return methodList;\n    }\n\n    @Override\n    public Set<String> getImportPackages(TableInfo tableInfo) {\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        Set<String> imports = new HashSet<>();\n        if (!singleIndex) {\n            imports.add(ObjectUtils.class.getName());\n        }\n        if (!globalConfig.isKotlin()) {\n            imports.add(List.class.getName());\n        }\n        if (globalConfig.isKotlin()) {\n            imports.add(KtQueryWrapper.class.getName());\n            imports.add(KtUpdateWrapper.class.getName());\n        } else {\n            imports.add(Wrappers.class.getName());\n        }\n        return imports;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/index/DefaultGenerateMapperMethodHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.index;\n\nimport com.baomidou.mybatisplus.core.toolkit.ObjectUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport com.baomidou.mybatisplus.generator.model.MapperMethod;\nimport com.baomidou.mybatisplus.generator.util.KotlinTypeUtils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 按字符串或者字符串常量方法生成查询条件\n * <p>复合索引下,第一个字段不会判空,后续字段会进行判空处理,也就是只能保证第一个字段不传递空,无法解决掉索引中间项传递为空的情况</p>\n * <p>由于需求不一样,默认只处理单字段索引,如果默认复合索引的方案符合你的要求,你可以考虑{@link #singleIndex}设置成false</p>\n *\n * @author nieqiurong\n * @see Entity.Builder#enableColumnConstant()\n * @since 3.5.10\n */\npublic class DefaultGenerateMapperMethodHandler extends AbstractMapperMethodHandler {\n\n    /**\n     * 只生成单索引字段方法(默认true)\n     * <p>当设置为true时,代表会过滤掉复合索引</p>\n     */\n    private final boolean singleIndex;\n\n    public DefaultGenerateMapperMethodHandler() {\n        this(true);\n    }\n\n    public DefaultGenerateMapperMethodHandler(boolean singleIndex) {\n        this.singleIndex = singleIndex;\n    }\n\n    @Override\n    public List<MapperMethod> getMethodList(TableInfo tableInfo) {\n        Map<String, List<DatabaseMetaDataWrapper.Index>> indexlistMap = tableInfo.getIndexList().stream()\n            .collect(Collectors.groupingBy(DatabaseMetaDataWrapper.Index::getName));\n        String entityName = tableInfo.getEntityName();\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        Entity entity = tableInfo.getStrategyConfig().entity();\n        boolean columnConstant = entity.isColumnConstant();\n        Set<Map.Entry<String, List<DatabaseMetaDataWrapper.Index>>> entrySet = indexlistMap.entrySet();\n        List<MapperMethod> methodList = new ArrayList<>();\n        for (Map.Entry<String, List<DatabaseMetaDataWrapper.Index>> entry : entrySet) {\n            String indexName = entry.getKey();\n            List<DatabaseMetaDataWrapper.Index> indexList = entry.getValue();\n            int indexSize = indexList.size();\n            if (this.singleIndex && indexSize > 1) {\n                continue;\n            }\n            // use byId\n            if (indexSize == 1 && isPrimaryKey(indexName)) {\n                continue;\n            }\n            List<TableField> tableFieldList = new ArrayList<>();\n            Map<String, TableField> tableFieldMap = tableInfo.getTableFieldMap();\n            StringBuilder baseMethodNameBuilder = new StringBuilder();\n            StringBuilder argsBuilder = new StringBuilder();\n            StringBuilder baseWrapperBuilder = new StringBuilder();\n            boolean uniqueKey = false;\n            for (int i = 0; i < indexSize; i++) {\n                DatabaseMetaDataWrapper.Index index = indexList.get(i);\n                if (index.isUnique()) {\n                    uniqueKey = true;\n                }\n                TableField tableField = tableFieldMap.get(index.getColumnName());\n                if (index.getColumnName().equals(entity.getLogicDeleteColumnName())\n                    || tableField.getPropertyName().equals(entity.getLogicDeletePropertyName())) {\n                    continue;\n                }\n                tableFieldList.add(tableField);\n                baseMethodNameBuilder.append(tableField.getCapitalName());\n                if (indexSize > 1) {\n                    if (columnConstant) {\n                        baseWrapperBuilder.append(\"eq(ObjectUtils.isNotNull(\").append(tableField.getPropertyName()).append(\")\").append(\", \").append(entityName).append(\".\").append(tableField.getName().toUpperCase());\n                    } else {\n                        baseWrapperBuilder.append(\"eq(ObjectUtils.isNotNull(\").append(tableField.getPropertyName()).append(\")\").append(\", \").append(\"\\\"\").append(tableField.getColumnName()).append(\"\\\"\");\n                    }\n                } else {\n                    if (columnConstant) {\n                        baseWrapperBuilder.append(\"eq(\").append(entityName).append(\".\").append(tableField.getName().toUpperCase());\n                    } else {\n                        baseWrapperBuilder.append(\"eq(\").append(\"\\\"\").append(tableField.getColumnName()).append(\"\\\"\");\n                    }\n                }\n                baseWrapperBuilder.append(\",\").append(\" \").append(tableField.getPropertyName()).append(\")\");\n                if (globalConfig.isKotlin()) {\n                    argsBuilder.append(tableField.getPropertyName()).append(\":\").append(\" \")\n                        .append(KotlinTypeUtils.getStringType(tableField.getColumnType()));\n                    if (i > 0) {\n                        argsBuilder.append(\"?\");\n                    }\n                } else {\n                    argsBuilder.append(tableField.getColumnType().getType()).append(\" \").append(tableField.getPropertyName());\n                }\n                if (i < indexSize - 1) {\n                    baseWrapperBuilder.append(\".\");\n                    baseMethodNameBuilder.append(\"And\");\n                    argsBuilder.append(\", \");\n                }\n            }\n            String baseMethodName = baseMethodNameBuilder.toString();\n            if (StringUtils.isBlank(baseMethodNameBuilder)) {\n                continue;\n            }\n            String args = argsBuilder.toString();\n            String baseWrapper = baseWrapperBuilder.toString();\n            boolean returnList = (indexSize > 1 || !uniqueKey);\n            if (globalConfig.isKotlin()) {\n                String selectByMethod;\n                if (returnList) {\n                    selectByMethod = buildKotlinMethod(\"selectBy\" + baseMethodName, args,\n                        \"List<\" + tableInfo.getEntityName() + \">?\", \"selectList(Wrappers.query<\" + tableInfo.getEntityName() + \">().\" + baseWrapper + \")\");\n                } else {\n                    selectByMethod = buildKotlinMethod(\"selectBy\" + baseMethodName, args,\n                        tableInfo.getEntityName() + \"?\", \"selectOne(Wrappers.query<\" + tableInfo.getEntityName() + \">().\" + baseWrapper + \")\");\n                }\n                String updateByMethod = buildKotlinMethod(\"updateBy\" + baseMethodName, \"entity:\" + \" \" + tableInfo.getEntityName() + \", \" + args,\n                    \"Int\", \"update(entity, Wrappers.update<\" + tableInfo.getEntityName() + \">().\" + baseWrapper + \")\");\n                String deleteByMethod = buildKotlinMethod(\"deleteBy\" + baseMethodName, args,\n                    \"Int\", \"delete(Wrappers.update<\" + tableInfo.getEntityName() + \">().\" + baseWrapper + \")\");\n                methodList.add(new MapperMethod(indexName, selectByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, updateByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, deleteByMethod, tableFieldList));\n            } else {\n                String selectByMethod;\n                if (returnList) {\n                    selectByMethod = buildMethod(\"selectBy\" + baseMethodName, args, \"List<\" + tableInfo.getEntityName() + \">\",\n                        \"selectList(Wrappers.<\" + tableInfo.getEntityName() + \">query().\" + baseWrapper + \")\");\n                } else {\n                    selectByMethod = buildMethod(\"selectBy\" + baseMethodName, args, tableInfo.getEntityName(),\n                        \"selectOne(Wrappers.<\" + tableInfo.getEntityName() + \">query().\" + baseWrapper + \")\");\n                }\n\n                String updateByMethod = buildMethod(\n                    \"updateBy\" + baseMethodName, tableInfo.getEntityName() + \" entity\" + \", \" + args, \"int\",\n                    \"update(entity, Wrappers.<\" + tableInfo.getEntityName() + \">update().\" + baseWrapper + \")\");\n                String deleteByMethod = buildMethod(\"deleteBy\" + baseMethodName, args, \"int\",\n                    \"delete(Wrappers.<\" + tableInfo.getEntityName() + \">update().\" + baseWrapper + \")\");\n                methodList.add(new MapperMethod(indexName, selectByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, updateByMethod, tableFieldList));\n                methodList.add(new MapperMethod(indexName, deleteByMethod, tableFieldList));\n            }\n        }\n        return methodList;\n    }\n\n    @Override\n    public Set<String> getImportPackages(TableInfo tableInfo) {\n        GlobalConfig globalConfig = tableInfo.getGlobalConfig();\n        Set<String> imports = new HashSet<>();\n        if (!singleIndex) {\n            imports.add(ObjectUtils.class.getName());\n        }\n        if (!globalConfig.isKotlin()) {\n            imports.add(List.class.getName());\n        }\n        imports.add(Wrappers.class.getName());\n        return imports;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/jdbc/DatabaseMetaDataWrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.jdbc;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.ToString;\nimport org.apache.ibatis.type.JdbcType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.*;\n\n/**\n * 数据库数据元包装类\n *\n * @author nieqiurong 2021/2/8.\n * @since 3.5.0\n */\npublic class DatabaseMetaDataWrapper {\n\n    private static final Logger logger = LoggerFactory.getLogger(DatabaseMetaDataWrapper.class);\n\n    @Getter\n    private final Connection connection;\n\n    private final DatabaseMetaData databaseMetaData;\n\n    //TODO 暂时只支持一种\n    private final String catalog;\n\n    //TODO 暂时只支持一种\n    private final String schema;\n\n    public DatabaseMetaDataWrapper(Connection connection, String schemaName) {\n        try {\n            if (null == connection) {\n                throw new RuntimeException(\"connection cannot be null\");\n            }\n            this.connection = connection;\n            this.databaseMetaData = connection.getMetaData();\n            this.catalog = connection.getCatalog();\n            this.schema = schemaName;\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void closeConnection() {\n        Optional.ofNullable(connection).ifPresent((con) -> {\n            try {\n                con.close();\n            } catch (SQLException sqlException) {\n                logger.error(\"close connection exception:\", sqlException);\n            }\n        });\n    }\n\n    public Map<String, Column> getColumnsInfo(String tableNamePattern, boolean queryPrimaryKey) {\n        return getColumnsInfo(this.catalog, this.schema, tableNamePattern, queryPrimaryKey);\n    }\n\n    public List<DatabaseMetaDataWrapper.Index> getIndex(String tableName) {\n        List<DatabaseMetaDataWrapper.Index> indexList = new ArrayList<>();\n        try (ResultSet resultSet = databaseMetaData.getIndexInfo(catalog, schema, tableName, false, false)) {\n            // clickhouse v2驱动没实现返回了个null\n            if (resultSet != null) {\n                while (resultSet.next()) {\n                    Index index = new Index(resultSet);\n                    // skip function index\n                    if (StringUtils.isNotBlank(index.getColumnName())) {\n                        indexList.add(index);\n                    }\n                }\n            }\n        } catch (SQLException e) {\n            logger.error(\"reading index information for [{}]:\", tableName, e);\n        }\n        return indexList;\n    }\n\n    /**\n     * 获取表字段信息\n     *\n     * @return 表字段信息 (小写字段名->字段信息)\n     */\n    public Map<String, Column> getColumnsInfo(String catalog, String schema, String tableName, boolean queryPrimaryKey) {\n        Set<String> primaryKeys = new HashSet<>();\n        if (queryPrimaryKey) {\n            try (ResultSet primaryKeysResultSet = databaseMetaData.getPrimaryKeys(catalog, schema, tableName)) {\n                while (primaryKeysResultSet.next()) {\n                    String columnName = primaryKeysResultSet.getString(\"COLUMN_NAME\");\n                    primaryKeys.add(columnName);\n                }\n                if (primaryKeys.size() > 1) {\n                    logger.warn(\"The current table [{}] has multiple primary keys defined.\", tableName);\n                }\n            } catch (SQLException e) {\n                throw new RuntimeException(e);\n            }\n        }\n        Map<String, Column> columnsInfoMap = new LinkedHashMap<>();\n        try (ResultSet resultSet = databaseMetaData.getColumns(catalog, schema, tableName, \"%\")) {\n            while (resultSet.next()) {\n                Column column = new Column();\n                String name = resultSet.getString(\"COLUMN_NAME\");\n                column.name = name;\n                column.primaryKey = primaryKeys.contains(name);\n                column.typeName = resultSet.getString(\"TYPE_NAME\");\n                int dataType = resultSet.getInt(\"DATA_TYPE\");\n                JdbcType jdbcType = JdbcType.forCode(dataType);\n                if (jdbcType == null) {\n                    // 不标准的类型,统一转为OTHER\n                    jdbcType = JdbcType.OTHER;\n                }\n                column.jdbcType = jdbcType;\n                column.length = resultSet.getInt(\"COLUMN_SIZE\");\n                column.scale = resultSet.getInt(\"DECIMAL_DIGITS\");\n                column.remarks = formatComment(resultSet.getString(\"REMARKS\"));\n                column.defaultValue = resultSet.getString(\"COLUMN_DEF\");\n                column.nullable = resultSet.getInt(\"NULLABLE\") == DatabaseMetaData.columnNullable;\n                column.generatedColumn = isGeneratedOrAutoIncrementColumn(resultSet, \"IS_GENERATEDCOLUMN\");\n                column.autoIncrement = isGeneratedOrAutoIncrementColumn(resultSet, \"IS_AUTOINCREMENT\");\n                columnsInfoMap.put(name.toLowerCase(), column);\n            }\n            return Collections.unmodifiableMap(columnsInfoMap);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private boolean isGeneratedOrAutoIncrementColumn(ResultSet resultSet, String columnLabel) {\n        try {\n            return \"YES\".equals(resultSet.getString(columnLabel));\n        } catch (SQLException e) {\n            // ignore\n        }\n        return false;\n    }\n\n    public String formatComment(String comment) {\n        return StringUtils.isBlank(comment) ? StringPool.EMPTY : comment.replaceAll(\"\\r\\n\", \"\\t\");\n    }\n\n    public Table getTableInfo(String tableName) {\n        return getTableInfo(this.catalog, this.schema, tableName);\n    }\n\n    public List<Table> getTables(String tableNamePattern, String[] types) {\n        return getTables(this.catalog, this.schema, tableNamePattern, types);\n    }\n\n    public List<Table> getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) {\n        List<Table> tables = new ArrayList<>();\n        try (ResultSet resultSet = databaseMetaData.getTables(catalog, schemaPattern, tableNamePattern, types)) {\n            Table table;\n            while (resultSet.next()) {\n                table = new Table();\n                table.name = resultSet.getString(\"TABLE_NAME\");\n                table.remarks = formatComment(resultSet.getString(\"REMARKS\"));\n                table.tableType = resultSet.getString(\"TABLE_TYPE\");\n                tables.add(table);\n            }\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        return tables;\n    }\n\n    public Table getTableInfo(String catalog, String schema, String tableName) {\n        Table table = new Table();\n        //TODO 后面要根据表是否为视图来查询，后面重构表查询策略。\n        try (ResultSet resultSet = databaseMetaData.getTables(catalog, schema, tableName, new String[]{\"TABLE\", \"VIEW\"})) {\n            table.name = tableName;\n            while (resultSet.next()) {\n                table.remarks = formatComment(resultSet.getString(\"REMARKS\"));\n                table.tableType = resultSet.getString(\"TABLE_TYPE\");\n            }\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        return table;\n    }\n\n    @Getter\n    public static class Table {\n\n        private String name;\n\n        private String remarks;\n\n        private String tableType;\n\n        public boolean isView() {\n            return \"VIEW\".equals(tableType);\n        }\n\n    }\n\n    @Getter\n    public static class Column {\n\n        private boolean primaryKey;\n\n        private boolean autoIncrement;\n\n        private String name;\n\n        private int length;\n\n        private boolean nullable;\n\n        private String remarks;\n\n        private String defaultValue;\n\n        private int scale;\n\n        private JdbcType jdbcType;\n\n        @Setter\n        private String typeName;\n\n        private boolean generatedColumn;\n\n    }\n\n    @Getter\n    @ToString\n    public static class Index {\n\n        /**\n         * 索引名\n         */\n        private final String name;\n\n        /**\n         * 是否唯一索引\n         */\n        private final boolean unique;\n\n        /**\n         * 索引字段\n         */\n        private final String columnName;\n\n        /**\n         * 排序方式 (A OR D OR null)\n         */\n        private final String ascOrDesc;\n\n        private final int cardinality;\n\n        private final int ordinalPosition;\n\n        public Index(ResultSet resultSet) throws SQLException {\n            this.unique = !resultSet.getBoolean(\"NON_UNIQUE\");\n            this.name = resultSet.getString(\"INDEX_NAME\");\n            this.columnName = resultSet.getString(\"COLUMN_NAME\");\n            this.ascOrDesc = resultSet.getString(\"ASC_OR_DESC\");\n            this.cardinality = resultSet.getInt(\"CARDINALITY\");\n            this.ordinalPosition = resultSet.getInt(\"ORDINAL_POSITION\");\n        }\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/keywords/BaseKeyWordsHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.keywords;\n\nimport com.baomidou.mybatisplus.generator.config.IKeyWordsHandler;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.*;\n\n/**\n * 基类关键字处理\n *\n * @author nieqiurong 2020/5/8.\n * @since 3.3.2\n */\npublic abstract class BaseKeyWordsHandler implements IKeyWordsHandler {\n\n    public final Set<String> keyWords;\n\n    public BaseKeyWordsHandler(@NotNull List<String> keyWords) {\n        this.keyWords = new HashSet<>(keyWords);\n    }\n\n    public BaseKeyWordsHandler(@NotNull Set<String> keyWords) {\n        this.keyWords = keyWords;\n    }\n\n    @Override\n    public @NotNull Collection<String> getKeyWords() {\n        return keyWords;\n    }\n\n    @Override\n    public boolean isKeyWords(@NotNull String columnName) {\n        return getKeyWords().contains(columnName.toUpperCase(Locale.ENGLISH));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/keywords/H2KeyWordsHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.keywords;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.*;\n\n/**\n * <a href=\"http://www.h2database.com/html/advanced.html#keywords\">h2数据库关键字处理</a>\n *\n * @author nieqiurong 2020/5/7.\n * @since 3.3.2\n */\npublic class H2KeyWordsHandler extends BaseKeyWordsHandler {\n\n    private static final List<String> KEY_WORDS = new ArrayList<>(Arrays.asList(\n        \"ALL\",\n        \"AND\",\n        \"ARRAY\",\n        \"AS\",\n        \"BETWEEN\",\n        \"BOTH\",\n        \"CASE\",\n        \"CHECK\",\n        \"CONSTRAINT\",\n        \"CROSS\",\n        \"CURRENT_CATALOG\",\n        \"CURRENT_DATE\",\n        \"CURRENT_SCHEMA\",\n        \"CURRENT_TIME\",\n        \"CURRENT_TIMESTAMP\",\n        \"CURRENT_USER\",\n        \"DISTINCT\",\n        \"EXCEPT\",\n        \"EXISTS\",\n        \"FALSE\",\n        \"FETCH\",\n        \"FILTER\",\n        \"FOR\",\n        \"FOREIGN\",\n        \"FROM\",\n        \"FULL\",\n        \"GROUP\",\n        \"GROUPS\",\n        \"HAVING\",\n        \"IF\",\n        \"ILIKE\",\n        \"IN\",\n        \"INNER\",\n        \"INTERSECT\",\n        \"INTERSECTS\",\n        \"INTERVAL\",\n        \"IS\",\n        \"JOIN\",\n        \"LEADING\",\n        \"LEFT\",\n        \"LIKE\",\n        \"LIMIT\",\n        \"LOCALTIME\",\n        \"LOCALTIMESTAMP\",\n        \"MINUS\",\n        \"NATURAL\",\n        \"NOT\",\n        \"NULL\",\n        \"OFFSET\",\n        \"ON\",\n        \"OR\",\n        \"ORDER\",\n        \"OVER\",\n        \"PARTITION\",\n        \"PRIMARY\",\n        \"QUALIFY\",\n        \"RANGE\",\n        \"REGEXP\",\n        \"RIGHT\",\n        \"ROW\",\n        \"_ROWID_\",\n        \"ROWNUM\",\n        \"ROWS\",\n        \"SELECT\",\n        \"SYSDATE\",\n        \"SYSTIME\",\n        \"SYSTIMESTAMP\",\n        \"TABLE\",\n        \"TODAY\",\n        \"TOP\",\n        \"TRAILING\",\n        \"TRUE\",\n        \"UNION\",\n        \"UNIQUE\",\n        \"UNKNOWN\",\n        \"USING\",\n        \"VALUES\",\n        \"WHERE\",\n        \"WINDOW\",\n        \"WITH\"\n    ));\n\n    public H2KeyWordsHandler() {\n        super(new HashSet<>(KEY_WORDS));\n    }\n\n    public H2KeyWordsHandler(@NotNull List<String> keyWords) {\n        super(new HashSet<>(keyWords));\n    }\n\n    public H2KeyWordsHandler(@NotNull Set<String> keyWords) {\n        super(keyWords);\n    }\n\n    @Override\n    public @NotNull String formatStyle() {\n        return \"\\\"%s\\\"\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/keywords/MySqlKeyWordsHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.keywords;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.*;\n\n/**\n * mysql关键字处理\n * <a href=\"https://dev.mysql.com/doc/refman/8.0/en/keywords.html\">这里选取了mysql8.0文档中的关键字和保留字（含移除）</a>\n *\n * @author nieqiurong 2020/5/7.\n * @since 3.3.2\n */\npublic class MySqlKeyWordsHandler extends BaseKeyWordsHandler {\n\n    private static final List<String> KEY_WORDS = new ArrayList<>(Arrays.asList(\n        \"ACCESSIBLE\",\n        \"ACCOUNT\",\n        \"ACTION\",\n        \"ACTIVE\",\n        \"ADD\",\n        \"ADMIN\",\n        \"AFTER\",\n        \"AGAINST\",\n        \"AGGREGATE\",\n        \"ALGORITHM\",\n        \"ALL\",\n        \"ALTER\",\n        \"ALWAYS\",\n        \"ANALYSE\",\n        \"ANALYZE\",\n        \"AND\",\n        \"ANY\",\n        \"ARRAY\",\n        \"AS\",\n        \"ASC\",\n        \"ASCII\",\n        \"ASENSITIVE\",\n        \"AT\",\n        \"ATTRIBUTE\",\n        \"AUTHENTICATION\",\n        \"AUTOEXTEND_SIZE\",\n        \"AUTO_INCREMENT\",\n        \"AVG\",\n        \"AVG_ROW_LENGTH\",\n        \"BACKUP\",\n        \"BEFORE\",\n        \"BEGIN\",\n        \"BETWEEN\",\n        \"BIGINT\",\n        \"BINARY\",\n        \"BINLOG\",\n        \"BIT\",\n        \"BLOB\",\n        \"BLOCK\",\n        \"BOOL\",\n        \"BOOLEAN\",\n        \"BOTH\",\n        \"BTREE\",\n        \"BUCKETS\",\n        \"BY\",\n        \"BYTE\",\n        \"CACHE\",\n        \"CALL\",\n        \"CASCADE\",\n        \"CASCADED\",\n        \"CASE\",\n        \"CATALOG_NAME\",\n        \"CHAIN\",\n        \"CHALLENGE_RESPONSE\",\n        \"CHANGE\",\n        \"CHANGED\",\n        \"CHANNEL\",\n        \"CHAR\",\n        \"CHARACTER\",\n        \"CHARSET\",\n        \"CHECK\",\n        \"CHECKSUM\",\n        \"CIPHER\",\n        \"CLASS_ORIGIN\",\n        \"CLIENT\",\n        \"CLONE\",\n        \"CLOSE\",\n        \"COALESCE\",\n        \"CODE\",\n        \"COLLATE\",\n        \"COLLATION\",\n        \"COLUMN\",\n        \"COLUMNS\",\n        \"COLUMN_FORMAT\",\n        \"COLUMN_NAME\",\n        \"COMMENT\",\n        \"COMMIT\",\n        \"COMMITTED\",\n        \"COMPACT\",\n        \"COMPLETION\",\n        \"COMPONENT\",\n        \"COMPRESSED\",\n        \"COMPRESSION\",\n        \"CONCURRENT\",\n        \"CONDITION\",\n        \"CONNECTION\",\n        \"CONSISTENT\",\n        \"CONSTRAINT\",\n        \"CONSTRAINT_CATALOG\",\n        \"CONSTRAINT_NAME\",\n        \"CONSTRAINT_SCHEMA\",\n        \"CONTAINS\",\n        \"CONTEXT\",\n        \"CONTINUE\",\n        \"CONVERT\",\n        \"CPU\",\n        \"CREATE\",\n        \"CROSS\",\n        \"CUBE\",\n        \"CUME_DIST\",\n        \"CURRENT\",\n        \"CURRENT_DATE\",\n        \"CURRENT_TIME\",\n        \"CURRENT_TIMESTAMP\",\n        \"CURRENT_USER\",\n        \"CURSOR\",\n        \"CURSOR_NAME\",\n        \"DATA\",\n        \"DATABASE\",\n        \"DATABASES\",\n        \"DATAFILE\",\n        \"DATE\",\n        \"DATETIME\",\n        \"DAY\",\n        \"DAY_HOUR\",\n        \"DAY_MICROSECOND\",\n        \"DAY_MINUTE\",\n        \"DAY_SECOND\",\n        \"DEALLOCATE\",\n        \"DEC\",\n        \"DECIMAL\",\n        \"DECLARE\",\n        \"DEFAULT\",\n        \"DEFAULT_AUTH\",\n        \"DEFINER\",\n        \"DEFINITION\",\n        \"DELAYED\",\n        \"DELAY_KEY_WRITE\",\n        \"DELETE\",\n        \"DENSE_RANK\",\n        \"DESC\",\n        \"DESCRIBE\",\n        \"DESCRIPTION\",\n        \"DES_KEY_FILE\",\n        \"DETERMINISTIC\",\n        \"DIAGNOSTICS\",\n        \"DIRECTORY\",\n        \"DISABLE\",\n        \"DISCARD\",\n        \"DISK\",\n        \"DISTINCT\",\n        \"DISTINCTROW\",\n        \"DIV\",\n        \"DO\",\n        \"DOUBLE\",\n        \"DROP\",\n        \"DUAL\",\n        \"DUMPFILE\",\n        \"DUPLICATE\",\n        \"DYNAMIC\",\n        \"EACH\",\n        \"ELSE\",\n        \"ELSEIF\",\n        \"EMPTY\",\n        \"ENABLE\",\n        \"ENCLOSED\",\n        \"ENCRYPTION\",\n        \"END\",\n        \"ENDS\",\n        \"ENFORCED\",\n        \"ENGINE\",\n        \"ENGINE_ATTRIBUTE\",\n        \"ENGINES\",\n        \"ENUM\",\n        \"ERROR\",\n        \"ERRORS\",\n        \"ESCAPE\",\n        \"ESCAPED\",\n        \"EVENT\",\n        \"EVENTS\",\n        \"EVERY\",\n        \"EXCEPT\",\n        \"EXCHANGE\",\n        \"EXCLUDE\",\n        \"EXECUTE\",\n        \"EXISTS\",\n        \"EXIT\",\n        \"EXPANSION\",\n        \"EXPIRE\",\n        \"EXPLAIN\",\n        \"EXPORT\",\n        \"EXTENDED\",\n        \"EXTENT_SIZE\",\n        \"FACTOR\",\n        \"FAILED_LOGIN_ATTEMPTS\",\n        \"FALSE\",\n        \"FAST\",\n        \"FAULTS\",\n        \"FETCH\",\n        \"FIELDS\",\n        \"FILE\",\n        \"FILE_BLOCK_SIZE\",\n        \"FILTER\",\n        \"FINISH\",\n        \"FIRST\",\n        \"FIRST_VALUE\",\n        \"FIXED\",\n        \"FLOAT\",\n        \"FLOAT4\",\n        \"FLOAT8\",\n        \"FLUSH\",\n        \"FOLLOWING\",\n        \"FOLLOWS\",\n        \"FOR\",\n        \"FORCE\",\n        \"FOREIGN\",\n        \"FORMAT\",\n        \"FOUND\",\n        \"FROM\",\n        \"FULL\",\n        \"FULLTEXT\",\n        \"FUNCTION\",\n        \"GENERAL\",\n        \"GENERATED\",\n        \"GEOMCOLLECTION\",\n        \"GEOMETRY\",\n        \"GEOMETRYCOLLECTION\",\n        \"GET\",\n        \"GET_FORMAT\",\n        \"GET_MASTER_PUBLIC_KEY\",\n        \"GET_SOURCE_PUBLIC_KEY\",\n        \"GLOBAL\",\n        \"GRANT\",\n        \"GRANTS\",\n        \"GROUP\",\n        \"GROUP_REPLICATION\",\n        \"GROUPING\",\n        \"GROUPS\",\n        \"GTID_ONLY\",\n        \"HANDLER\",\n        \"HASH\",\n        \"HAVING\",\n        \"HELP\",\n        \"HIGH_PRIORITY\",\n        \"HISTOGRAM\",\n        \"HISTORY\",\n        \"HOST\",\n        \"HOSTS\",\n        \"HOUR\",\n        \"HOUR_MICROSECOND\",\n        \"HOUR_MINUTE\",\n        \"HOUR_SECOND\",\n        \"IDENTIFIED\",\n        \"IF\",\n        \"IGNORE\",\n        \"IGNORE_SERVER_IDS\",\n        \"IMPORT\",\n        \"IN\",\n        \"INACTIVE\",\n        \"INDEX\",\n        \"INDEXES\",\n        \"INFILE\",\n        \"INITIAL\",\n        \"INITIAL_SIZE\",\n        \"INITIATE\",\n        \"INNER\",\n        \"INOUT\",\n        \"INSENSITIVE\",\n        \"INSERT\",\n        \"INSERT_METHOD\",\n        \"INSTALL\",\n        \"INSTANCE\",\n        \"INT\",\n        \"INT1\",\n        \"INT2\",\n        \"INT3\",\n        \"INT4\",\n        \"INT8\",\n        \"INTEGER\",\n        \"INTERVAL\",\n        \"INTO\",\n        \"INVISIBLE\",\n        \"INVOKER\",\n        \"IO\",\n        \"IO_AFTER_GTIDS\",\n        \"IO_BEFORE_GTIDS\",\n        \"IO_THREAD\",\n        \"IPC\",\n        \"IS\",\n        \"ISOLATION\",\n        \"ISSUER\",\n        \"ITERATE\",\n        \"JOIN\",\n        \"JSON\",\n        \"JSON_TABLE\",\n        \"JSON_VALUE\",\n        \"KEY\",\n        \"KEYS\",\n        \"KEY_BLOCK_SIZE\",\n        \"KEYRING\",\n        \"KILL\",\n        \"LAG\",\n        \"LANGUAGE\",\n        \"LAST\",\n        \"LAST_VALUE\",\n        \"LATERAL\",\n        \"LEAD\",\n        \"LEADING\",\n        \"LEAVE\",\n        \"LEAVES\",\n        \"LEFT\",\n        \"LESS\",\n        \"LEVEL\",\n        \"LIKE\",\n        \"LIMIT\",\n        \"LINEAR\",\n        \"LINES\",\n        \"LINESTRING\",\n        \"LIST\",\n        \"LOAD\",\n        \"LOCAL\",\n        \"LOCALTIME\",\n        \"LOCALTIMESTAMP\",\n        \"LOCK\",\n        \"LOCKED\",\n        \"LOCKS\",\n        \"LOGFILE\",\n        \"LOGS\",\n        \"LONG\",\n        \"LONGBLOB\",\n        \"LONGTEXT\",\n        \"LOOP\",\n        \"LOW_PRIORITY\",\n        \"MASTER\",\n        \"MASTER_AUTO_POSITION\",\n        \"MASTER_BIND\",\n        \"MASTER_COMPRESSION_ALGORITHMS\",\n        \"MASTER_CONNECT_RETRY\",\n        \"MASTER_DELAY\",\n        \"MASTER_HEARTBEAT_PERIOD\",\n        \"MASTER_HOST\",\n        \"MASTER_LOG_FILE\",\n        \"MASTER_LOG_POS\",\n        \"MASTER_PASSWORD\",\n        \"MASTER_PORT\",\n        \"MASTER_PUBLIC_KEY_PATH\",\n        \"MASTER_RETRY_COUNT\",\n        \"MASTER_SERVER_ID\",\n        \"MASTER_SSL\",\n        \"MASTER_SSL_CA\",\n        \"MASTER_SSL_CAPATH\",\n        \"MASTER_SSL_CERT\",\n        \"MASTER_SSL_CIPHER\",\n        \"MASTER_SSL_CRL\",\n        \"MASTER_SSL_CRLPATH\",\n        \"MASTER_SSL_KEY\",\n        \"MASTER_SSL_VERIFY_SERVER_CERT\",\n        \"MASTER_TLS_CIPHERSUITES\",\n        \"MASTER_TLS_VERSION\",\n        \"MASTER_USER\",\n        \"MASTER_ZSTD_COMPRESSION_LEVEL\",\n        \"MATCH\",\n        \"MAXVALUE\",\n        \"MAX_CONNECTIONS_PER_HOUR\",\n        \"MAX_QUERIES_PER_HOUR\",\n        \"MAX_ROWS\",\n        \"MAX_SIZE\",\n        \"MAX_STATEMENT_TIME\",\n        \"MAX_UPDATES_PER_HOUR\",\n        \"MAX_USER_CONNECTIONS\",\n        \"MEDIUM\",\n        \"MEDIUMBLOB\",\n        \"MEDIUMINT\",\n        \"MEDIUMTEXT\",\n        \"MEMBER\",\n        \"MEMORY\",\n        \"MERGE\",\n        \"MESSAGE_TEXT\",\n        \"MICROSECOND\",\n        \"MIDDLEINT\",\n        \"MIGRATE\",\n        \"MINUTE\",\n        \"MINUTE_MICROSECOND\",\n        \"MINUTE_SECOND\",\n        \"MIN_ROWS\",\n        \"MOD\",\n        \"MODE\",\n        \"MODIFIES\",\n        \"MODIFY\",\n        \"MONTH\",\n        \"MULTILINESTRING\",\n        \"MULTIPOINT\",\n        \"MULTIPOLYGON\",\n        \"MUTEX\",\n        \"MYSQL_ERRNO\",\n        \"NAME\",\n        \"NAMES\",\n        \"NATIONAL\",\n        \"NATURAL\",\n        \"NCHAR\",\n        \"NDB\",\n        \"NDBCLUSTER\",\n        \"NESTED\",\n        \"NETWORK_NAMESPACE\",\n        \"NEVER\",\n        \"NEW\",\n        \"NEXT\",\n        \"NO\",\n        \"NODEGROUP\",\n        \"NONBLOCKING\",\n        \"NONE\",\n        \"NOT\",\n        \"NOWAIT\",\n        \"NO_WAIT\",\n        \"NO_WRITE_TO_BINLOG\",\n        \"NTH_VALUE\",\n        \"NTILE\",\n        \"NULL\",\n        \"NULLS\",\n        \"NUMBER\",\n        \"NUMERIC\",\n        \"NVARCHAR\",\n        \"OF\",\n        \"OFF\",\n        \"OFFSET\",\n        \"OJ\",\n        \"OLD\",\n        \"OLD_PASSWORD\",\n        \"ON\",\n        \"ONE\",\n        \"ONLY\",\n        \"OPEN\",\n        \"OPTIMIZE\",\n        \"OPTIMIZER_COSTS\",\n        \"OPTION\",\n        \"OPTIONAL\",\n        \"OPTIONALLY\",\n        \"OPTIONS\",\n        \"OR\",\n        \"ORDER\",\n        \"ORDINALITY\",\n        \"ORGANIZATION\",\n        \"OTHERS\",\n        \"OUT\",\n        \"OUTER\",\n        \"OUTFILE\",\n        \"OVER\",\n        \"OWNER\",\n        \"PACK_KEYS\",\n        \"PAGE\",\n        \"PARSER\",\n        \"PARSE_GCOL_EXPR\",\n        \"PARTIAL\",\n        \"PARTITION\",\n        \"PARTITIONING\",\n        \"PARTITIONS\",\n        \"PASSWORD\",\n        \"PASSWORD_LOCK_TIME\",\n        \"PATH\",\n        \"PERCENT_RANK\",\n        \"PERSIST\",\n        \"PERSIST_ONLY\",\n        \"PHASE\",\n        \"PLUGIN\",\n        \"PLUGINS\",\n        \"PLUGIN_DIR\",\n        \"POINT\",\n        \"POLYGON\",\n        \"PORT\",\n        \"PRECEDES\",\n        \"PRECEDING\",\n        \"PRECISION\",\n        \"PREPARE\",\n        \"PRESERVE\",\n        \"PREV\",\n        \"PRIMARY\",\n        \"PRIVILEGE_CHECKS_USER\",\n        \"PRIVILEGES\",\n        \"PROCEDURE\",\n        \"PROCESS\",\n        \"PROCESSLIST\",\n        \"PROFILE\",\n        \"PROFILES\",\n        \"PROXY\",\n        \"PURGE\",\n        \"QUARTER\",\n        \"QUERY\",\n        \"QUICK\",\n        \"RANDOM\",\n        \"RANGE\",\n        \"RANK\",\n        \"READ\",\n        \"READS\",\n        \"READ_ONLY\",\n        \"READ_WRITE\",\n        \"REAL\",\n        \"REBUILD\",\n        \"RECOVER\",\n        \"RECURSIVE\",\n        \"REDOFILE\",\n        \"REDO_BUFFER_SIZE\",\n        \"REDUNDANT\",\n        \"REFERENCE\",\n        \"REFERENCES\",\n        \"REGEXP\",\n        \"REGISTRATION\",\n        \"RELAY\",\n        \"RELAYLOG\",\n        \"RELAY_LOG_FILE\",\n        \"RELAY_LOG_POS\",\n        \"RELAY_THREAD\",\n        \"RELEASE\",\n        \"RELOAD\",\n        \"REMOVE\",\n        \"RENAME\",\n        \"REORGANIZE\",\n        \"REPAIR\",\n        \"REPEAT\",\n        \"REPEATABLE\",\n        \"REPLACE\",\n        \"REPLICA\",\n        \"REPLICAS\",\n        \"REPLICATE_DO_DB\",\n        \"REPLICATE_DO_TABLE\",\n        \"REPLICATE_IGNORE_DB\",\n        \"REPLICATE_IGNORE_TABLE\",\n        \"REPLICATE_REWRITE_DB\",\n        \"REPLICATE_WILD_DO_TABLE\",\n        \"REPLICATE_WILD_IGNORE_TABLE\",\n        \"REPLICATION\",\n        \"REQUIRE\",\n        \"REQUIRE_ROW_FORMAT\",\n        \"RESET\",\n        \"RESIGNAL\",\n        \"RESOURCE\",\n        \"RESPECT\",\n        \"RESTART\",\n        \"RESTORE\",\n        \"RESTRICT\",\n        \"RESUME\",\n        \"RETAIN\",\n        \"RETURN\",\n        \"RETURNED_SQLSTATE\",\n        \"RETURNING\",\n        \"RETURNS\",\n        \"REUSE\",\n        \"REVERSE\",\n        \"REVOKE\",\n        \"RIGHT\",\n        \"RLIKE\",\n        \"ROLE\",\n        \"ROLLBACK\",\n        \"ROLLUP\",\n        \"ROTATE\",\n        \"ROUTINE\",\n        \"ROW\",\n        \"ROWS\",\n        \"ROW_COUNT\",\n        \"ROW_FORMAT\",\n        \"ROW_NUMBER\",\n        \"RTREE\",\n        \"SAVEPOINT\",\n        \"SCHEDULE\",\n        \"SCHEMA\",\n        \"SCHEMAS\",\n        \"SCHEMA_NAME\",\n        \"SECOND\",\n        \"SECOND_MICROSECOND\",\n        \"SECONDARY\",\n        \"SECONDARY_ENGINE\",\n        \"SECONDARY_ENGINE_ATTRIBUTE\",\n        \"SECONDARY_LOAD\",\n        \"SECONDARY_UNLOAD\",\n        \"SECURITY\",\n        \"SELECT\",\n        \"SENSITIVE\",\n        \"SEPARATOR\",\n        \"SERIAL\",\n        \"SERIALIZABLE\",\n        \"SERVER\",\n        \"SESSION\",\n        \"SET\",\n        \"SHARE\",\n        \"SHOW\",\n        \"SHUTDOWN\",\n        \"SIGNAL\",\n        \"SIGNED\",\n        \"SIMPLE\",\n        \"SKIP\",\n        \"SLAVE\",\n        \"SLOW\",\n        \"SMALLINT\",\n        \"SNAPSHOT\",\n        \"SOCKET\",\n        \"SOME\",\n        \"SONAME\",\n        \"SOUNDS\",\n        \"SOURCE\",\n        \"SOURCE_AUTO_POSITION\",\n        \"SOURCE_BIND\",\n        \"SOURCE_COMPRESSION_ALGORITHMS\",\n        \"SOURCE_CONNECT_RETRY\",\n        \"SOURCE_DELAY\",\n        \"SOURCE_HEARTBEAT_PERIOD\",\n        \"SOURCE_HOST\",\n        \"SOURCE_LOG_FILE\",\n        \"SOURCE_LOG_POS\",\n        \"SOURCE_PASSWORD\",\n        \"SOURCE_PORT\",\n        \"SOURCE_PUBLIC_KEY_PATH\",\n        \"SOURCE_RETRY_COUNT\",\n        \"SOURCE_SSL\",\n        \"SOURCE_SSL_CA\",\n        \"SOURCE_SSL_CAPATH\",\n        \"SOURCE_SSL_CERT\",\n        \"SOURCE_SSL_CIPHER\",\n        \"SOURCE_SSL_CRL\",\n        \"SOURCE_SSL_CRLPATH\",\n        \"SOURCE_SSL_KEY\",\n        \"SOURCE_SSL_VERIFY_SERVER_CERT\",\n        \"SOURCE_TLS_CIPHERSUITES\",\n        \"SOURCE_TLS_VERSION\",\n        \"SOURCE_USER\",\n        \"SOURCE_ZSTD_COMPRESSION_LEVEL\",\n        \"SPATIAL\",\n        \"SPECIFIC\",\n        \"SQL\",\n        \"SQLEXCEPTION\",\n        \"SQLSTATE\",\n        \"SQLWARNING\",\n        \"SRID\",\n        \"SQL_AFTER_GTIDS\",\n        \"SQL_AFTER_MTS_GAPS\",\n        \"SQL_BEFORE_GTIDS\",\n        \"SQL_BIG_RESULT\",\n        \"SQL_BUFFER_RESULT\",\n        \"SQL_CACHE\",\n        \"SQL_CALC_FOUND_ROWS\",\n        \"SQL_NO_CACHE\",\n        \"SQL_SMALL_RESULT\",\n        \"SQL_THREAD\",\n        \"SQL_TSI_DAY\",\n        \"SQL_TSI_HOUR\",\n        \"SQL_TSI_MINUTE\",\n        \"SQL_TSI_MONTH\",\n        \"SQL_TSI_QUARTER\",\n        \"SQL_TSI_SECOND\",\n        \"SQL_TSI_WEEK\",\n        \"SQL_TSI_YEAR\",\n        \"SSL\",\n        \"STACKED\",\n        \"START\",\n        \"STARTING\",\n        \"STARTS\",\n        \"STATS_AUTO_RECALC\",\n        \"STATS_PERSISTENT\",\n        \"STATS_SAMPLE_PAGES\",\n        \"STATUS\",\n        \"STOP\",\n        \"STORAGE\",\n        \"STORED\",\n        \"STRAIGHT_JOIN\",\n        \"STREAM\",\n        \"STRING\",\n        \"SUBCLASS_ORIGIN\",\n        \"SUBJECT\",\n        \"SUBPARTITION\",\n        \"SUBPARTITIONS\",\n        \"SUPER\",\n        \"SUSPEND\",\n        \"SWAPS\",\n        \"SWITCHES\",\n        \"SYSTEM\",\n        \"TABLE\",\n        \"TABLES\",\n        \"TABLESPACE\",\n        \"TABLE_CHECKSUM\",\n        \"TABLE_NAME\",\n        \"TEMPORARY\",\n        \"TEMPTABLE\",\n        \"TERMINATED\",\n        \"TEXT\",\n        \"THAN\",\n        \"THEN\",\n        \"THREAD_PRIORITY\",\n        \"TIES\",\n        \"TIME\",\n        \"TIMESTAMP\",\n        \"TIMESTAMPADD\",\n        \"TIMESTAMPDIFF\",\n        \"TINYBLOB\",\n        \"TINYINT\",\n        \"TINYTEXT\",\n        \"TLS\",\n        \"TO\",\n        \"TRAILING\",\n        \"TRANSACTION\",\n        \"TRIGGER\",\n        \"TRIGGERS\",\n        \"TRUE\",\n        \"TRUNCATE\",\n        \"TYPE\",\n        \"TYPES\",\n        \"UNBOUNDED\",\n        \"UNCOMMITTED\",\n        \"UNDEFINED\",\n        \"UNDO\",\n        \"UNDOFILE\",\n        \"UNDO_BUFFER_SIZE\",\n        \"UNICODE\",\n        \"UNINSTALL\",\n        \"UNION\",\n        \"UNIQUE\",\n        \"UNKNOWN\",\n        \"UNLOCK\",\n        \"UNREGISTER\",\n        \"UNSIGNED\",\n        \"UNTIL\",\n        \"UPDATE\",\n        \"UPGRADE\",\n        \"USAGE\",\n        \"USE\",\n        \"USER\",\n        \"USER_RESOURCES\",\n        \"USE_FRM\",\n        \"USING\",\n        \"UTC_DATE\",\n        \"UTC_TIME\",\n        \"UTC_TIMESTAMP\",\n        \"VALIDATION\",\n        \"VALUE\",\n        \"VALUES\",\n        \"VARBINARY\",\n        \"VARCHAR\",\n        \"VARCHARACTER\",\n        \"VARIABLES\",\n        \"VARYING\",\n        \"VCPU\",\n        \"VIEW\",\n        \"VIRTUAL\",\n        \"VISIBLE\",\n        \"WAIT\",\n        \"WARNINGS\",\n        \"WEEK\",\n        \"WEIGHT_STRING\",\n        \"WHEN\",\n        \"WHERE\",\n        \"WHILE\",\n        \"WINDOW\",\n        \"WITH\",\n        \"WITHOUT\",\n        \"WORK\",\n        \"WRAPPER\",\n        \"WRITE\",\n        \"X509\",\n        \"XA\",\n        \"XID\",\n        \"XML\",\n        \"XOR\",\n        \"YEAR\",\n        \"YEAR_MONTH\",\n        \"ZEROFILL\",\n        \"ZONE\"));\n\n    public MySqlKeyWordsHandler() {\n        super(new HashSet<>(KEY_WORDS));\n    }\n\n    public MySqlKeyWordsHandler(@NotNull List<String> keyWords) {\n        super(new HashSet<>(keyWords));\n    }\n\n    public MySqlKeyWordsHandler(@NotNull Set<String> keyWords) {\n        super(keyWords);\n    }\n\n\n    @Override\n    public @NotNull String formatStyle() {\n        return \"`%s`\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/keywords/PostgreSqlKeyWordsHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.keywords;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.*;\n\n/**\n * <a href=\"https://www.postgresql.org/docs/11/sql-keywords-appendix.html\">postgresql关键字处理</a>\n *\n * @author nieqiurong 2020/5/9.\n * @since 3.3.2\n */\npublic class PostgreSqlKeyWordsHandler extends BaseKeyWordsHandler {\n\n    private static final List<String> KEY_WORDS = new ArrayList<>(Arrays.asList(\n        \"ALL\",\n        \"ANALYSE\",\n        \"ANALYZE\",\n        \"AND\",\n        \"ANY\",\n        \"ARRAY\",\n        \"AS\",\n        \"ASC\",\n        \"ASYMMETRIC\",\n        \"AUTHORIZATION\",\n        \"BINARY\",\n        \"BOTH\",\n        \"CASE\",\n        \"CAST\",\n        \"CHECK\",\n        \"COLLATE\",\n        \"COLLATION\",\n        \"COLUMN\",\n        \"CONCURRENTLY\",\n        \"CONSTRAINT\",\n        \"CREATE\",\n        \"CROSS\",\n        \"CURRENT_CATALOG\",\n        \"CURRENT_DATE\",\n        \"CURRENT_ROLE\",\n        \"CURRENT_SCHEMA\",\n        \"CURRENT_TIME\",\n        \"CURRENT_TIMESTAMP\",\n        \"CURRENT_USER\",\n        \"DEFAULT\",\n        \"DEFERRABLE\",\n        \"DESC\",\n        \"DISTINCT\",\n        \"DO\",\n        \"ELSE\",\n        \"END\",\n        \"EXCEPT\",\n        \"FALSE\",\n        \"FETCH\",\n        \"FOR\",\n        \"FOREIGN\",\n        \"FREEZE\",\n        \"FROM\",\n        \"FULL\",\n        \"GRANT\",\n        \"GROUP\",\n        \"HAVING\",\n        \"ILIKE\",\n        \"IN\",\n        \"INITIALLY\",\n        \"INNER\",\n        \"INTERSECT\",\n        \"INTO\",\n        \"IS\",\n        \"ISNULL\",\n        \"JOIN\",\n        \"LATERAL\",\n        \"LEADING\",\n        \"LEFT\",\n        \"LIKE\",\n        \"LIMIT\",\n        \"LOCALTIME\",\n        \"LOCALTIMESTAMP\",\n        \"NATURAL\",\n        \"NOT\",\n        \"NOTNULL\",\n        \"NULL\",\n        \"OFFSET\",\n        \"ON\",\n        \"ONLY\",\n        \"OR\",\n        \"ORDER\",\n        \"OUTER\",\n        \"OVERLAPS\",\n        \"PLACING\",\n        \"PRIMARY\",\n        \"REFERENCES\",\n        \"RETURNING\",\n        \"RIGHT\",\n        \"SELECT\",\n        \"SESSION_USER\",\n        \"SIMILAR\",\n        \"SOME\",\n        \"SYMMETRIC\",\n        \"TABLE\",\n        \"TABLESAMPLE\",\n        \"THEN\",\n        \"TO\",\n        \"TRAILING\",\n        \"TRUE\",\n        \"UNION\",\n        \"UNIQUE\",\n        \"USER\",\n        \"USING\",\n        \"VARIADIC\",\n        \"VERBOSE\",\n        \"WHEN\",\n        \"WHERE\",\n        \"WINDOW\",\n        \"WITH\"\n    ));\n\n    public PostgreSqlKeyWordsHandler() {\n        super(new HashSet<>(KEY_WORDS));\n    }\n\n    public PostgreSqlKeyWordsHandler(@NotNull List<String> keyWords) {\n        super(new HashSet<>(keyWords));\n    }\n\n    public PostgreSqlKeyWordsHandler(@NotNull Set<String> keyWords) {\n        super(keyWords);\n    }\n\n    @Override\n    public @NotNull String formatStyle() {\n        return \"\\\"%s\\\"\";\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/keywords/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 数据库关键字处理\n * 由于只是生成代码上简单使用，目前简单的实现一下。<br/>\n * 随着数据库版本的不同，关键字会有新增或移除，建议去查询对应数据库版本文档来制订关键字集合。\n *\n * @author nieqiurong 2020/5/8.\n */\npackage com.baomidou.mybatisplus.generator.keywords;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/model/AnnotationAttributes.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.model;\n\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.ToString;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 注解属性\n *\n * @author nieqiurong\n * @since 3.5.10\n */\n@Getter\n@ToString\npublic class AnnotationAttributes {\n\n    /**\n     * 显示名称\n     */\n    @Setter\n    private String displayName;\n\n    /**\n     * 导入包路径\n     */\n    private final Set<String> importPackages = new HashSet<>();\n\n    public AnnotationAttributes() {\n    }\n\n    public AnnotationAttributes(@NotNull Class<?> annotationClass) {\n        this.displayName = \"@\" + annotationClass.getSimpleName();\n        this.importPackages.add(annotationClass.getName());\n    }\n    public AnnotationAttributes(@NotNull Class<?> annotationClass, @NotNull String displayName, String... extraPkg) {\n        this.displayName = displayName;\n        this.importPackages.add(annotationClass.getName());\n        if (extraPkg != null && extraPkg.length > 0) {\n            this.importPackages.addAll(Arrays.asList(extraPkg));\n        }\n    }\n\n    public AnnotationAttributes(@NotNull String displayName, @NotNull String... importPackages) {\n        this.displayName = displayName;\n        this.importPackages.addAll(Arrays.asList(importPackages));\n    }\n\n    /**\n     * 添加导包\n     *\n     * @param importPackage 包路径\n     */\n    public void addImportPackage(@NotNull String importPackage) {\n        this.importPackages.add(importPackage);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/model/ClassAnnotationAttributes.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.model;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\nimport lombok.ToString;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * 类注解属性\n *\n * @author nieqiurong\n * @since 3.5.10\n */\n@Setter\n@Getter\n@ToString\n@NoArgsConstructor\npublic class ClassAnnotationAttributes extends AnnotationAttributes {\n\n    /**\n     * 根据{@link TableInfo}信息加工处理显示注解\n     * <p>显示名称处理函数(最终需要转换成{@link #setDisplayName(String)})<p/>\n     */\n    private Function<TableInfo, String> displayNameFunction;\n\n    public ClassAnnotationAttributes(@NotNull Class<?> annotationClass) {\n        super(annotationClass);\n    }\n\n    public ClassAnnotationAttributes(@NotNull Class<?> annotationClass, @NotNull Function<TableInfo, String> displayNameFunction, String... extraPkg) {\n        super(annotationClass);\n        this.displayNameFunction = displayNameFunction;\n        if (extraPkg != null && extraPkg.length > 0) {\n            super.getImportPackages().addAll(Arrays.asList(extraPkg));\n        }\n    }\n\n    public ClassAnnotationAttributes(@NotNull Class<?> annotationClass, @NotNull String displayName, String... extraPkg) {\n        super(annotationClass, displayName, extraPkg);\n    }\n\n    public ClassAnnotationAttributes(@NotNull String displayName, @NotNull String... importPackages) {\n        super(displayName, importPackages);\n    }\n\n    public ClassAnnotationAttributes(@NotNull String importPackage, @NotNull Function<TableInfo, String> displayNameFunction) {\n        super.getImportPackages().add(importPackage);\n        this.displayNameFunction = displayNameFunction;\n    }\n\n    public ClassAnnotationAttributes(@NotNull Set<String> importPackages, @NotNull Function<TableInfo, String> displayNameFunction) {\n        super.getImportPackages().addAll(importPackages);\n        this.displayNameFunction = displayNameFunction;\n    }\n\n    public void handleDisplayName(TableInfo tableInfo) {\n        if (displayNameFunction != null) {\n            super.setDisplayName(displayNameFunction.apply(tableInfo));\n        }\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/model/MapperMethod.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.model;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Unmodifiable;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author nieqiurong\n * @since 3.5.10\n */\npublic class MapperMethod {\n\n    /**\n     * 索引名称\n     */\n    @Getter\n    @NotNull\n    private final String indexName;\n\n    /**\n     * 方法体\n     */\n    @Getter\n    @NotNull\n    private final String method;\n\n    /**\n     * 索引字段信息\n     */\n    @NotNull\n    private final List<TableField> tableFieldList = new ArrayList<>();\n\n    /**\n     * 扩展参数\n     */\n    @NotNull\n    private final Map<String, Object> extendData = new HashMap<>();\n\n    public MapperMethod(@NotNull String indexName, @NotNull String method, @NotNull List<TableField> tableFieldList) {\n        this.indexName = indexName;\n        this.method = method;\n        this.tableFieldList.addAll(tableFieldList);\n    }\n\n    public void addExtendData(@NotNull String key, @NotNull Object data) {\n        this.extendData.put(key, data);\n    }\n\n    public @Unmodifiable @NotNull Map<String, Object> getExtendData() {\n        return Collections.unmodifiableMap(this.extendData);\n    }\n\n    public @Unmodifiable @NotNull List<TableField> getTableFieldList() {\n        return Collections.unmodifiableList(this.tableFieldList);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 代码生成器相关类\n */\npackage com.baomidou.mybatisplus.generator;\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/query/AbstractDatabaseQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.query;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * 数据库查询抽象类\n *\n * @author nieqiurong\n * @since 3.5.3\n */\npublic abstract class AbstractDatabaseQuery implements IDatabaseQuery {\n\n    protected final Logger LOGGER = LoggerFactory.getLogger(getClass());\n\n    protected final ConfigBuilder configBuilder;\n\n    protected final DataSourceConfig dataSourceConfig;\n\n    protected final StrategyConfig strategyConfig;\n\n    protected final GlobalConfig globalConfig;\n\n\n    public AbstractDatabaseQuery(@NotNull ConfigBuilder configBuilder) {\n        this.configBuilder = configBuilder;\n        this.dataSourceConfig = configBuilder.getDataSourceConfig();\n        this.strategyConfig = configBuilder.getStrategyConfig();\n        this.globalConfig = configBuilder.getGlobalConfig();\n    }\n\n    @NotNull\n    public ConfigBuilder getConfigBuilder() {\n        return configBuilder;\n    }\n\n    @NotNull\n    public DataSourceConfig getDataSourceConfig() {\n        return dataSourceConfig;\n    }\n\n    protected void filter(List<TableInfo> tableList, List<TableInfo> includeTableList, List<TableInfo> excludeTableList) {\n        boolean isInclude = !strategyConfig.getInclude().isEmpty();\n        boolean isExclude = !strategyConfig.getExclude().isEmpty();\n        if (isExclude || isInclude) {\n            Map<String, String> notExistTables = new HashSet<>(isExclude ? strategyConfig.getExclude() : strategyConfig.getInclude())\n                .stream()\n                .filter(s -> !ConfigBuilder.matcherRegTable(s))\n                .collect(Collectors.toMap(String::toLowerCase, s -> s, (o, n) -> n));\n            // 将已经存在的表移除，获取配置中数据库不存在的表\n            for (TableInfo tabInfo : tableList) {\n                if (notExistTables.isEmpty()) {\n                    break;\n                }\n                //解决可能大小写不敏感的情况导致无法移除掉\n                notExistTables.remove(tabInfo.getName().toLowerCase());\n            }\n            if (!notExistTables.isEmpty()) {\n                LOGGER.warn(\"Table [{}] does not exist in the database!\", String.join(StringPool.COMMA, notExistTables.values()));\n            }\n            // 需要反向生成的表信息\n            if (isExclude) {\n                tableList.removeAll(excludeTableList);\n            } else {\n                tableList.clear();\n                tableList.addAll(includeTableList);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/query/DefaultQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.query;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport com.baomidou.mybatisplus.generator.type.ITypeConvertHandler;\nimport com.baomidou.mybatisplus.generator.type.TypeRegistry;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 元数据查询数据库信息.\n *\n * @author nieqiurong 2022/5/11.\n * @see ITypeConvertHandler 类型转换器(如果默认逻辑不能满足，可实现此接口重写类型转换)\n * <p>\n * 测试通过的数据库：H2、Mysql-5.7.37、Mysql-8.0.25、PostgreSQL-11.15、PostgreSQL-14.1、Oracle-11.2.0.1.0、DM8\n * </p>\n * <p>\n * FAQ:\n * 1.Mysql无法读取表注释: 链接增加属性 remarks=true&useInformationSchema=true 或者通过{@link DataSourceConfig.Builder#addConnectionProperty(String, String)}设置\n * 2.Oracle无法读取注释: 增加属性remarks=true，也有些驱动版本说是增加remarksReporting=true {@link DataSourceConfig.Builder#addConnectionProperty(String, String)}\n * </p>\n * @since 3.5.3\n */\npublic class DefaultQuery extends AbstractDatabaseQuery {\n\n    private final TypeRegistry typeRegistry;\n    protected final DatabaseMetaDataWrapper databaseMetaDataWrapper;\n\n    public DefaultQuery(@NotNull ConfigBuilder configBuilder) {\n        super(configBuilder);\n        typeRegistry = new TypeRegistry(configBuilder.getGlobalConfig());\n        this.databaseMetaDataWrapper = new DatabaseMetaDataWrapper(dataSourceConfig.getConn(), dataSourceConfig.getSchemaName());\n    }\n\n    @Override\n    public @NotNull List<TableInfo> queryTables() {\n        try {\n            boolean isInclude = !strategyConfig.getInclude().isEmpty();\n            boolean isExclude = !strategyConfig.getExclude().isEmpty();\n            //所有的表信息\n            List<TableInfo> tableList = new ArrayList<>();\n            List<DatabaseMetaDataWrapper.Table> tables = this.getTables();\n            //需要反向生成或排除的表信息\n            List<TableInfo> includeTableList = new ArrayList<>();\n            List<TableInfo> excludeTableList = new ArrayList<>();\n            tables.forEach(table -> {\n                String tableName = table.getName();\n                if (StringUtils.isNotBlank(tableName)) {\n                    TableInfo tableInfo = new TableInfo(this.configBuilder, tableName);\n                    tableInfo.setComment(table.getRemarks());\n                    if (isInclude && strategyConfig.matchIncludeTable(tableName)) {\n                        includeTableList.add(tableInfo);\n                    } else if (isExclude && strategyConfig.matchExcludeTable(tableName)) {\n                        excludeTableList.add(tableInfo);\n                    }\n                    tableList.add(tableInfo);\n                }\n            });\n            filter(tableList, includeTableList, excludeTableList);\n            // 性能优化，只处理需执行表字段 https://github.com/baomidou/mybatis-plus/issues/219\n            tableList.forEach(this::convertTableFields);\n            return tableList;\n        } finally {\n            // 数据库操作完成,释放连接对象\n            databaseMetaDataWrapper.closeConnection();\n        }\n    }\n\n    protected List<DatabaseMetaDataWrapper.Table> getTables() {\n        // 是否跳过视图\n        boolean skipView = strategyConfig.isSkipView();\n        // 获取表过滤\n        String tableNamePattern = null;\n        if (strategyConfig.getLikeTable() != null) {\n            tableNamePattern = strategyConfig.getLikeTable().getValue();\n        }\n        if (strategyConfig.getNotLikeTable() != null) {\n            LOGGER.warn(\"Unsupported 'notLikeTable' configuration\");\n        }\n        return databaseMetaDataWrapper.getTables(tableNamePattern, skipView ? new String[]{\"TABLE\"} : new String[]{\"TABLE\", \"VIEW\"});\n    }\n\n    protected void convertTableFields(@NotNull TableInfo tableInfo) {\n        String tableName = tableInfo.getName();\n        Map<String, DatabaseMetaDataWrapper.Column> columnsInfoMap = getColumnsInfo(tableName);\n        Entity entity = strategyConfig.entity();\n        columnsInfoMap.forEach((k, columnInfo) -> {\n            TableField.MetaInfo metaInfo = new TableField.MetaInfo(columnInfo, tableInfo);\n            String columnName = columnInfo.getName();\n            TableField field = new TableField(this.configBuilder, columnName);\n            // 处理ID\n            if (columnInfo.isPrimaryKey()) {\n                field.primaryKey(columnInfo.isAutoIncrement());\n                tableInfo.setHavePrimaryKey(true);\n                if (field.isKeyIdentityFlag() && entity.getIdType() != null) {\n                    LOGGER.warn(\"The primary key of the current table [{}] is configured as auto-incrementing, which will override the global primary key ID type strategy.\", tableName);\n                }\n            }\n            IColumnType columnType;\n            ITypeConvertHandler typeConvertHandler = dataSourceConfig.getTypeConvertHandler();\n            if (typeConvertHandler != null) {\n                columnType = typeConvertHandler.convert(globalConfig, typeRegistry, metaInfo);\n            } else {\n                columnType = typeRegistry.getColumnType(metaInfo);\n            }\n            field.setColumnName(columnName).setColumnType(columnType).setComment(columnInfo.getRemarks()).setMetaInfo(metaInfo);\n            String propertyName = entity.getNameConvert().propertyNameConvert(field);\n            field.setPropertyName(propertyName, columnType);\n            tableInfo.addField(field);\n        });\n        tableInfo.setIndexList(getIndex(tableName));\n        tableInfo.processTable();\n    }\n\n    protected Map<String, DatabaseMetaDataWrapper.Column> getColumnsInfo(String tableName) {\n        return databaseMetaDataWrapper.getColumnsInfo(tableName, true);\n    }\n\n    protected List<DatabaseMetaDataWrapper.Index> getIndex(String tableName) {\n        return databaseMetaDataWrapper.getIndex(tableName);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/query/IDatabaseQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.query;\n\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong 2021/1/6.\n * @since 3.5.0\n */\npublic interface IDatabaseQuery {\n\n    /**\n     * 获取表信息\n     *\n     * @return 表信息\n     */\n    @NotNull\n    List<TableInfo> queryTables();\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/query/SQLQuery.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.query;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.generator.config.IDbQuery;\nimport com.baomidou.mybatisplus.generator.config.ITypeConvert;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.Entity;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.querys.DbQueryDecorator;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport com.baomidou.mybatisplus.generator.jdbc.DatabaseMetaDataWrapper;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.sql.SQLException;\nimport java.util.*;\n\n/**\n * 这是兼容以前旧版本提供的查询方式，需要每个数据库对接适配。\n *\n * @author nieqiurong 2021/1/6.\n * @see IDbQuery 数据库适配\n * @see ITypeConvert 类型适配处理\n * @since 3.5.0\n */\npublic class SQLQuery extends AbstractDatabaseQuery {\n\n    protected final DbQueryDecorator dbQuery;\n    protected final DatabaseMetaDataWrapper databaseMetaDataWrapper;\n\n    public SQLQuery(@NotNull ConfigBuilder configBuilder) {\n        super(configBuilder);\n        this.dbQuery = new DbQueryDecorator(dataSourceConfig, strategyConfig);\n        this.databaseMetaDataWrapper = new DatabaseMetaDataWrapper(dbQuery.getConnection(), dataSourceConfig.getSchemaName());\n    }\n\n    @NotNull\n    @Override\n    public List<TableInfo> queryTables() {\n        boolean isInclude = !strategyConfig.getInclude().isEmpty();\n        boolean isExclude = !strategyConfig.getExclude().isEmpty();\n        //所有的表信息\n        List<TableInfo> tableList = new ArrayList<>();\n\n        //需要反向生成或排除的表信息\n        List<TableInfo> includeTableList = new ArrayList<>();\n        List<TableInfo> excludeTableList = new ArrayList<>();\n        try {\n            dbQuery.execute(dbQuery.tablesSql(), result -> {\n                String tableName = result.getStringResult(dbQuery.tableName());\n                if (StringUtils.isNotBlank(tableName)) {\n                    TableInfo tableInfo = new TableInfo(this.configBuilder, tableName);\n                    String tableComment = result.getTableComment();\n                    // 跳过视图\n                    if (!(strategyConfig.isSkipView() && tableComment.toUpperCase().contains(\"VIEW\"))) {\n                        tableInfo.setComment(tableComment);\n                        if (isInclude && strategyConfig.matchIncludeTable(tableName)) {\n                            includeTableList.add(tableInfo);\n                        } else if (isExclude && strategyConfig.matchExcludeTable(tableName)) {\n                            excludeTableList.add(tableInfo);\n                        }\n                        tableList.add(tableInfo);\n                    }\n                }\n            });\n            filter(tableList, includeTableList, excludeTableList);\n            // 性能优化，只处理需执行表字段 https://github.com/baomidou/mybatis-plus/issues/219\n            tableList.forEach(this::convertTableFields);\n            return tableList;\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        } finally {\n            // 数据库操作完成,释放连接对象\n            dbQuery.closeConnection();\n        }\n    }\n\n    protected void convertTableFields(@NotNull TableInfo tableInfo) {\n        String tableName = tableInfo.getName();\n        try {\n            Map<String, DatabaseMetaDataWrapper.Column> columnsInfoMap = databaseMetaDataWrapper.getColumnsInfo(tableName, false);\n            String tableFieldsSql = dbQuery.tableFieldsSql(tableName);\n            Set<String> pkColumns = new HashSet<>();\n            String primaryKeySql = dbQuery.primaryKeySql(dataSourceConfig, tableName);\n            boolean selectPk = StringUtils.isNotBlank(primaryKeySql);\n            if (selectPk) {\n                dbQuery.execute(primaryKeySql, result -> {\n                    String primaryKey = result.getStringResult(dbQuery.fieldKey());\n                    if (Boolean.parseBoolean(primaryKey)) {\n                        pkColumns.add(result.getStringResult(dbQuery.fieldName()));\n                    }\n                });\n            }\n            Entity entity = strategyConfig.entity();\n            dbQuery.execute(tableFieldsSql, result -> {\n                String columnName = result.getStringResult(dbQuery.fieldName());\n                TableField field = new TableField(this.configBuilder, columnName);\n                DatabaseMetaDataWrapper.Column columnInfo = columnsInfoMap.get(columnName.toLowerCase());\n                // 设置字段的元数据信息\n                TableField.MetaInfo metaInfo = new TableField.MetaInfo(columnInfo, tableInfo);\n                // 避免多重主键设置，目前只取第一个找到ID，并放到list中的索引为0的位置\n                boolean isId = selectPk ? pkColumns.contains(columnName) : result.isPrimaryKey();\n                // 处理ID\n                if (isId) {\n                    field.primaryKey(dbQuery.isKeyIdentity(result.getResultSet()));\n                    tableInfo.setHavePrimaryKey(true);\n                    if (field.isKeyIdentityFlag() && entity.getIdType() != null) {\n                        LOGGER.warn(\"The primary key of the current table [{}] is configured as auto-incrementing, which will override the global primary key ID type strategy.\", tableName);\n                    }\n                }\n                // 处理ID\n                field.setColumnName(columnName)\n                    .setType(result.getStringResult(dbQuery.fieldType()))\n                    .setComment(result.getFiledComment())\n                    .setCustomMap(dbQuery.getCustomFields(result.getResultSet()));\n                String propertyName = entity.getNameConvert().propertyNameConvert(field);\n                IColumnType columnType = dataSourceConfig.getTypeConvert().processTypeConvert(globalConfig, field);\n                field.setPropertyName(propertyName, columnType);\n                field.setMetaInfo(metaInfo);\n                tableInfo.addField(field);\n            });\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n        tableInfo.processTable();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/type/ITypeConvertHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.type;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * 类型转换处理器\n *\n * @author nieqiurong 2022/5/12.\n * @since 3.5.3\n */\npublic interface ITypeConvertHandler {\n\n    /**\n     * 转换字段类型\n     *\n     * @param globalConfig 全局配置\n     * @param typeRegistry 类型注册信息\n     * @param metaInfo     字段元数据信息\n     * @return 子类类型\n     */\n    @NotNull\n    IColumnType convert(GlobalConfig globalConfig, TypeRegistry typeRegistry, TableField.MetaInfo metaInfo);\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/type/TypeRegistry.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.type;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\n\nimport java.sql.Types;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 类型注册处理类\n *\n * @author nieqiurong 2022/5/11.\n */\npublic class TypeRegistry {\n\n    private final GlobalConfig globalConfig;\n\n    private final Map<Integer, IColumnType> typeMap = new HashMap<>();\n\n    public TypeRegistry(GlobalConfig globalConfig) {\n        this.globalConfig = globalConfig;\n        // byte[]\n        typeMap.put(Types.BINARY, DbColumnType.BYTE_ARRAY);\n        typeMap.put(Types.BLOB, DbColumnType.BYTE_ARRAY);\n        typeMap.put(Types.LONGVARBINARY, DbColumnType.BYTE_ARRAY);\n        typeMap.put(Types.VARBINARY, DbColumnType.BYTE_ARRAY);\n        //byte\n        typeMap.put(Types.TINYINT, DbColumnType.BYTE);\n        //long\n        typeMap.put(Types.BIGINT, DbColumnType.LONG);\n        //boolean\n        typeMap.put(Types.BIT, DbColumnType.BOOLEAN);\n        typeMap.put(Types.BOOLEAN, DbColumnType.BOOLEAN);\n        //short\n        typeMap.put(Types.SMALLINT, DbColumnType.SHORT);\n        //string\n        typeMap.put(Types.CHAR, DbColumnType.STRING);\n        typeMap.put(Types.CLOB, DbColumnType.STRING);\n        typeMap.put(Types.VARCHAR, DbColumnType.STRING);\n        typeMap.put(Types.LONGVARCHAR, DbColumnType.STRING);\n        typeMap.put(Types.LONGNVARCHAR, DbColumnType.STRING);\n        typeMap.put(Types.NCHAR, DbColumnType.STRING);\n        typeMap.put(Types.NCLOB, DbColumnType.STRING);\n        typeMap.put(Types.NVARCHAR, DbColumnType.STRING);\n        //date\n        typeMap.put(Types.DATE, DbColumnType.DATE);\n        //timestamp\n        typeMap.put(Types.TIMESTAMP, DbColumnType.TIMESTAMP);\n        typeMap.put(Types.TIMESTAMP_WITH_TIMEZONE, DbColumnType.TIMESTAMP);\n        //double\n        typeMap.put(Types.FLOAT, DbColumnType.DOUBLE);\n        typeMap.put(Types.REAL, DbColumnType.DOUBLE);\n        typeMap.put(Types.DOUBLE, DbColumnType.DOUBLE);\n        //int\n        typeMap.put(Types.INTEGER, DbColumnType.INTEGER);\n        //bigDecimal\n        typeMap.put(Types.NUMERIC, DbColumnType.BIG_DECIMAL);\n        typeMap.put(Types.DECIMAL, DbColumnType.BIG_DECIMAL);\n        //TODO 类型需要补充完整\n    }\n\n    public IColumnType getColumnType(TableField.MetaInfo metaInfo, DbColumnType defaultType) {\n        //TODO 是否用包装类??? 可以尝试判断字段是否允许为null来判断是否用包装类\n        int typeCode = metaInfo.getJdbcType().TYPE_CODE;\n        switch (typeCode) {\n            // TODO 需要增加类型处理，尚未补充完整\n            case Types.BIT:\n                return getBitType(metaInfo);\n            case Types.DATE:\n                return getDateType(metaInfo);\n            case Types.TIME:\n                return getTimeType(metaInfo);\n            case Types.DECIMAL:\n            case Types.NUMERIC:\n                return getNumber(metaInfo);\n            case Types.TIMESTAMP:\n                return getTimestampType(metaInfo);\n            default:\n                return typeMap.getOrDefault(typeCode, defaultType);\n        }\n    }\n\n    public IColumnType getColumnType(TableField.MetaInfo metaInfo) {\n        return getColumnType(metaInfo, DbColumnType.OBJECT);\n    }\n\n    private IColumnType getBitType(TableField.MetaInfo metaInfo) {\n        if (metaInfo.getLength() > 1) {\n            return DbColumnType.BYTE_ARRAY;\n        }\n        return DbColumnType.BOOLEAN;\n    }\n\n    private IColumnType getNumber(TableField.MetaInfo metaInfo) {\n        if (metaInfo.getScale() > 0 || metaInfo.getLength() > 18) {\n            return typeMap.get(metaInfo.getJdbcType().TYPE_CODE);\n        } else if (metaInfo.getLength() > 9) {\n            return DbColumnType.LONG;\n        } else if (metaInfo.getLength() > 4) {\n            return DbColumnType.INTEGER;\n        } else {\n            return DbColumnType.SHORT;\n        }\n    }\n\n    private IColumnType getDateType(TableField.MetaInfo metaInfo) {\n        DbColumnType dbColumnType;\n        DateType dateType = globalConfig.getDateType();\n        switch (dateType) {\n            case SQL_PACK:\n                dbColumnType = DbColumnType.DATE_SQL;\n                break;\n            case TIME_PACK:\n                dbColumnType = DbColumnType.LOCAL_DATE;\n                break;\n            default:\n                dbColumnType = DbColumnType.DATE;\n        }\n        return dbColumnType;\n    }\n\n    private IColumnType getTimeType(TableField.MetaInfo metaInfo) {\n        DbColumnType dbColumnType;\n        DateType dateType = globalConfig.getDateType();\n        if (dateType == DateType.TIME_PACK) {\n            dbColumnType = DbColumnType.LOCAL_TIME;\n        } else {\n            dbColumnType = DbColumnType.TIME;\n        }\n        return dbColumnType;\n    }\n\n    private IColumnType getTimestampType(TableField.MetaInfo metaInfo) {\n        DbColumnType dbColumnType;\n        DateType dateType = globalConfig.getDateType();\n        if (dateType == DateType.TIME_PACK) {\n            dbColumnType = DbColumnType.LOCAL_DATE_TIME;\n        } else if (dateType == DateType.ONLY_DATE) {\n            dbColumnType = DbColumnType.DATE;\n        } else {\n            dbColumnType = DbColumnType.TIMESTAMP;\n        }\n        return dbColumnType;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/util/ClassUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.util;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\n\n/**\n * @author nieqiurong 2020/11/9.\n * @since 3.5.0\n */\npublic final class ClassUtils {\n\n    private ClassUtils() {\n    }\n\n\n    /**\n     * 获取类名\n     *\n     * @param className className 全类名\n     * @return ignore\n     */\n    public static String getSimpleName(String className) {\n        return StringUtils.isBlank(className) ? null : className.substring(className.lastIndexOf(StringPool.DOT) + 1);\n    }\n\n\n    /**\n     * <p>\n     * 请仅在确定类存在的情况下调用该方法\n     * </p>\n     *\n     * @param name 类名称\n     * @return 返回转换后的 Class\n     */\n    public static Class<?> toClassConfident(String name) {\n        return com.baomidou.mybatisplus.core.toolkit.ClassUtils.toClassConfident(name);\n    }\n\n    /**\n     * Return the default ClassLoader to use: typically the thread context\n     * ClassLoader, if available; the ClassLoader that loaded the ClassUtils\n     * class will be used as fallback.\n     * <p>Call this method if you intend to use the thread context ClassLoader\n     * in a scenario where you clearly prefer a non-null ClassLoader reference:\n     * for example, for class path resource loading (but not necessarily for\n     * {@code Class.forName}, which accepts a {@code null} ClassLoader\n     * reference as well).\n     *\n     * @return the default ClassLoader (only {@code null} if even the system\n     * ClassLoader isn't accessible)\n     * @see Thread#getContextClassLoader()\n     * @see ClassLoader#getSystemClassLoader()\n     * @since 3.3.2\n     * @deprecated 3.5.13\n     */\n    @Deprecated\n    public static ClassLoader getDefaultClassLoader() {\n        ClassLoader cl = null;\n        try {\n            cl = Thread.currentThread().getContextClassLoader();\n        } catch (Throwable ex) {\n            // Cannot access thread context ClassLoader - falling back...\n        }\n        if (cl == null) {\n            // No thread context class loader -> use class loader of this class.\n            cl = ClassUtils.class.getClassLoader();\n            if (cl == null) {\n                // getClassLoader() returning null indicates the bootstrap ClassLoader\n                try {\n                    cl = ClassLoader.getSystemClassLoader();\n                } catch (Throwable ex) {\n                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...\n                }\n            }\n        }\n        return cl;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/util/FileUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.util;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * 文件处理工具类 {@link FileUtils}\n *\n * @since 3.5.0\n */\npublic class FileUtils {\n\n    /**\n     * Makes a directory, including any necessary but nonexistent parent\n     * directories. If a file already exists with specified name but it is\n     * not a directory then an IOException is thrown.\n     * If the directory cannot be created (or does not already exist)\n     * then an IOException is thrown.\n     *\n     * @param directory directory to create, must not be {@code null}\n     * @throws NullPointerException if the directory is {@code null}\n     * @throws IOException          if the directory cannot be created or the file already exists but is not a directory\n     */\n    public static void forceMkdir(final File directory) throws IOException {\n        if (directory.exists()) {\n            if (!directory.isDirectory()) {\n                final String message =\n                    \"File \"\n                        + directory\n                        + \" exists and is \"\n                        + \"not a directory. Unable to create directory.\";\n                throw new IOException(message);\n            }\n        } else {\n            if (!directory.mkdirs()) {\n                // Double-check that some other thread or process hasn't made\n                // the directory in the background\n                if (!directory.isDirectory()) {\n                    final String message =\n                        \"Unable to create directory \" + directory;\n                    throw new IOException(message);\n                }\n            }\n        }\n    }\n\n    /**\n     * Makes any necessary but nonexistent parent directories for a given File. If the parent directory cannot be\n     * created then an IOException is thrown.\n     *\n     * @param file file with parent to create, must not be {@code null}\n     * @throws NullPointerException if the file is {@code null}\n     * @throws IOException          if the parent directory cannot be created\n     * @since 2.5\n     */\n    public static void forceMkdirParent(final File file) throws IOException {\n        final File parent = file.getParentFile();\n        if (parent == null) {\n            return;\n        }\n        forceMkdir(parent);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/util/KotlinTypeUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.util;\n\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Kotlin类型处理工具类\n *\n * @author nieqiurong\n * @since 3.5.10\n */\npublic class KotlinTypeUtils {\n\n    private static final Map<IColumnType, String> JAVA_TO_KOTLIN_TYPE = new HashMap<>();\n\n    static {\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_INT, \"Int\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.INTEGER, \"Int\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_DOUBLE, \"Double\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_FLOAT, \"Float\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_LONG, \"Long\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_BOOLEAN, \"Boolean\");\n        JAVA_TO_KOTLIN_TYPE.put(DbColumnType.BASE_CHAR, \"Char\");\n    }\n\n    /**\n     * 转换Java类型至Kotlin类型\n     *\n     * @param columnType {@link DbColumnType}\n     * @return Kotlin类型\n     */\n    public static String getStringType(IColumnType columnType) {\n        return JAVA_TO_KOTLIN_TYPE.getOrDefault(columnType, columnType.getType());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/java/com/baomidou/mybatisplus/generator/util/RuntimeUtils.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.generator.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.text.MessageFormat;\n\n/**\n * 运行工具类\n *\n * @author nieqiurong 2020/11/13.\n * @since 3.5.0\n */\npublic class RuntimeUtils {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeUtils.class);\n\n    /**\n     * 打开指定输出文件目录\n     *\n     * @param outDir 输出文件目录\n     */\n    public static void openDir(String outDir) throws IOException {\n        File file = new File(outDir);\n        if (!file.isDirectory()) {\n            LOGGER.error(\"illegal directory:{}\", outDir);\n            throw new IllegalArgumentException(\"Illegal directory \" + outDir);\n        }\n        String osName = System.getProperty(\"os.name\");\n        if (osName != null) {\n            if (osName.contains(\"Mac\")) {\n                Runtime.getRuntime().exec(\"open \" + outDir);\n            } else if (osName.contains(\"Windows\")) {\n                Runtime.getRuntime().exec(MessageFormat.format(\"cmd /c start \\\"\\\" \\\"{0}\\\"\", outDir));\n            } else {\n                LOGGER.debug(\"file output directory:{}\", outDir);\n            }\n        } else {\n            LOGGER.warn(\"read operating system failed!\");\n        }\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/controller.java.btl",
    "content": "package ${package.Controller};\n\nimport org.springframework.web.bind.annotation.RequestMapping;\n<% if(restControllerStyle){ %>\nimport org.springframework.web.bind.annotation.RestController;\n<% }else{ %>\nimport org.springframework.stereotype.Controller;\n<% } %>\n<% if(isNotEmpty(superControllerClassPackage)){ %>\nimport ${superControllerClassPackage};\n<% } %>\n\n/**\n * <p>\n * ${table.comment!} 前端控制器\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<% if(restControllerStyle){ %>\n@RestController\n<% }else{ %>\n@Controller\n<% } %>\n@RequestMapping(\"<% if(isNotEmpty(package.ModuleName)){ %>/${package.ModuleName}<% } %>/<% if(controllerMappingHyphenStyle){ %>${controllerMappingHyphen}<% }else{ %>${table.entityPath}<% } %>\")\n<% if(kotlin){ %>\nclass ${table.controllerName}<% if(isNotEmpty(superControllerClass)){ %> : ${superControllerClass}()<% } %>\n<% }else{ %>\n    <% if(isNotEmpty(superControllerClass)){ %>\npublic class ${table.controllerName} extends ${superControllerClass} {\n    <% }else{ %>\npublic class ${table.controllerName} {\n    <% } %>\n\n}\n<% } %>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/controller.java.ej",
    "content": "package #(package.Controller);\n\nimport org.springframework.web.bind.annotation.RequestMapping;\n#if(restControllerStyle)\nimport org.springframework.web.bind.annotation.RestController;\n#else\nimport org.springframework.stereotype.Controller;\n#end\n#if(superControllerClassPackage)\nimport #(superControllerClassPackage);\n#end\n\n/**\n * <p>\n * #(table.comment ??) 前端控制器\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n#if(restControllerStyle)\n@RestController\n#else\n@Controller\n#end\n@RequestMapping(\"#if(package.ModuleName)/#(package.ModuleName)#end/#if(controllerMappingHyphenStyle)#(controllerMappingHyphen)#else#(table.entityPath)#end\")\n#if(kotlin)\nclass #(table.controllerName)#if(superControllerClass) : #(superControllerClass)()#end\n#else\n#if(superControllerClass)\npublic class #(table.controllerName) extends #(superControllerClass) {\n#else\npublic class #(table.controllerName) {\n#end\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/controller.java.ftl",
    "content": "package ${package.Controller};\n\nimport org.springframework.web.bind.annotation.RequestMapping;\n<#if restControllerStyle>\nimport org.springframework.web.bind.annotation.RestController;\n<#else>\nimport org.springframework.stereotype.Controller;\n</#if>\n<#if superControllerClassPackage??>\nimport ${superControllerClassPackage};\n</#if>\n\n/**\n * <p>\n * ${table.comment!} 前端控制器\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#if restControllerStyle>\n@RestController\n<#else>\n@Controller\n</#if>\n@RequestMapping(\"<#if package.ModuleName?? && package.ModuleName != \"\">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}</#if>\")\n<#if kotlin>\nclass ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>\n<#else>\n<#if superControllerClass??>\npublic class ${table.controllerName} extends ${superControllerClass} {\n<#else>\npublic class ${table.controllerName} {\n</#if>\n\n}\n</#if>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/controller.java.vm",
    "content": "package ${package.Controller};\n\nimport org.springframework.web.bind.annotation.RequestMapping;\n#if(${restControllerStyle})\nimport org.springframework.web.bind.annotation.RestController;\n#else\nimport org.springframework.stereotype.Controller;\n#end\n#if(${superControllerClassPackage})\nimport ${superControllerClassPackage};\n#end\n\n/**\n * <p>\n * $!{table.comment} 前端控制器\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#if(${restControllerStyle})\n@RestController\n#else\n@Controller\n#end\n@RequestMapping(\"#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end\")\n#if(${kotlin})\nclass ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end\n#else\n#if(${superControllerClass})\npublic class ${table.controllerName} extends ${superControllerClass} {\n#else\npublic class ${table.controllerName} {\n#end\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.java.btl",
    "content": "package ${package.Entity};\n\n<% for(pkg in importEntityFrameworkPackages){ %>\nimport ${pkg};\n<% } %>\n\n<% for(pkg in importEntityJavaPackages){ %>\nimport ${pkg};\n<% } %>\n\n/**\n * <p>\n * ${table.comment!}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<% for(an in entityClassAnnotations){ %>\n${an.displayName}\n<% } %>\n<% if(isNotEmpty(superEntityClass)){ %>\npublic class ${entity} extends ${superEntityClass}<% if(activeRecord){ %><${entity}><%}%>{\n<% }else if(activeRecord){ %>\npublic class ${entity} extends Model<${entity}> {\n<% }else if(entitySerialVersionUID){ %>\npublic class ${entity} implements Serializable {\n<% }else{ %>\npublic class ${entity} {\n<% } %>\n<% if(entitySerialVersionUID){ %>\n\n    <% if(entitySerialAnnotation) { %>\n    @Serial\n    <% } %>\n    private static final long serialVersionUID = 1L;\n<% } %>\n<% var keyPropertyName; %>\n<% /** -----------BEGIN 字段循环遍历----------- **/ %>\n<% for(field in table.fields){ %>\n\n    <%\n    if(field.keyFlag){\n        keyPropertyName = field.propertyName;\n    }\n    %>\n    <% if(isNotEmpty(field.comment)){ %>\n        <% if(entityFieldUseJavaDoc){ %>\n    /**\n     * ${field.comment}\n     */\n        <% } %>\n    <% } %>\n    <% for(an in field.annotationAttributesList){ %>\n    ${an.displayName}\n    <% } %>\n    private ${field.propertyType} ${field.propertyName};\n<% } %>\n<% /** -----------END 字段循环遍历----------- **/ %>\n<% if(!entityLombokModel){ %>\n    <% for(field in table.fields){ %>\n        <%\n        var getprefix ='';\n        if(field.propertyType=='boolean'){\n            getprefix='is';\n        }else{\n            getprefix='get';\n        }\n        %>\n\n    public ${field.propertyType} ${getprefix}${field.capitalName}() {\n        return ${field.propertyName};\n    }\n\n        <% if(chainModel){ %>\n    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n        <% }else{ %>\n    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n        <% } %>\n        this.${field.propertyName} = ${field.propertyName};\n        <% if(chainModel){ %>\n        return this;\n        <% } %>\n    }\n    <% } %>\n<% } %>\n<% if(entityColumnConstant){ %>\n   <% for(field in table.fields){ %>\n\n    public static final String ${strutil.toUpperCase(field.name)} = \"${field.name}\";\n   <% } %>\n<% } %>\n<% if(activeRecord){ %>\n\n    @Override\n    public Serializable pkVal() {\n    <% if(isNotEmpty(keyPropertyName)){ %>\n        return this.${keyPropertyName};\n    <% }else{ %>\n        return null;\n    <% } %>\n    }\n<% } %>\n<% if(!entityLombokModel && entityToString){ %>\n\n    @Override\n    public String toString() {\n        return \"${entity}{\" +\n    <% for(field in table.fields){ %>\n       <% if(fieldLP.dataIndex==0){ %>\n            \"${field.propertyName} = \" + ${field.propertyName} +\n       <% }else{ %>\n            \", ${field.propertyName} = \" + ${field.propertyName} +\n       <% } %>\n    <% } %>\n            \"}\";\n    }\n<% } %>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.java.ej",
    "content": "package #(package.Entity);\n\n#for(pkg : importEntityFrameworkPackages)\nimport #(pkg);\n#end\n\n#for(pkg : importEntityJavaPackages)\nimport #(pkg);\n#end\n\n/**\n * <p>\n * #(table.comment)\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n#for(an : entityClassAnnotations)\n#(an.displayName)\n#end\n#if(superEntityClass)\npublic class #(entity) extends #(superEntityClass)#if(activeRecord)<#(entity)>#end {\n#elseif(activeRecord)\npublic class #(entity) extends Model<#(entity)> {\n#elseif(entitySerialVersionUID)\npublic class #(entity) implements Serializable {\n#else\npublic class #(entity) {\n#end\n#if(entitySerialVersionUID)\n\n    #if(entitySerialAnnotation)\n    @Serial\n    #end\n    private static final long serialVersionUID = 1L;\n#end\n### ----------  BEGIN 字段循环遍历  ----------\n#for(field : table.fields)\n\n#if(field.isKeyFlag())\n#set(keyPropertyName = field.propertyName)\n#end\n#if(field.comment != null)\n  #if(entityFieldUseJavaDoc)\n    /**\n     * #(field.comment)\n     */\n  #end\n#end\n#for(an : field.annotationAttributesList)\n    #(an.displayName)\n#end\n    private #(field.propertyType) #(field.propertyName);\n#end\n### ----------  END 字段循环遍历  ----------\n#if(!entityLombokModel)\n#for(field : table.fields)\n  #if(field.propertyType.equals(\"boolean\"))\n    #set(getprefix=\"is\")\n  #else\n    #set(getprefix=\"get\")\n  #end\n\n    public #(field.propertyType) #(getprefix)#(field.capitalName)() {\n        return #(field.propertyName);\n    }\n\n  #if(chainModel)\n    public #(entity) set#(field.capitalName)(#(field.propertyType) #(field.propertyName)) {\n  #else\n    public void set#(field.capitalName)(#(field.propertyType) #(field.propertyName)) {\n  #end\n        this.#(field.propertyName) = #(field.propertyName);\n  #if(chainModel)\n        return this;\n  #end\n    }\n#end\n### --foreach end---\n#end\n### --end of #if(entityLombokModel)--\n#if(entityColumnConstant)\n  #for(field : table.fields)\n\n    public static final String #(field.name.toUpperCase()) = \"#(field.name)\";\n  #end\n#end\n#if(activeRecord)\n\n    @Override\n    public Serializable pkVal() {\n  #if(keyPropertyName)\n        return this.#(keyPropertyName);\n  #else\n        return null;\n  #end\n    }\n#end\n#if(!entityLombokModel && entityToString)\n\n    @Override\n    public String toString() {\n        return \"#(entity){\" +\n  #for(field : table.fields)\n    #if(for.index == 0)\n            \"#(field.propertyName) = \" + #(field.propertyName) +\n    #else\n            \", #(field.propertyName) = \" + #(field.propertyName) +\n    #end\n  #end\n            \"}\";\n    }\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.java.ftl",
    "content": "package ${package.Entity};\n\n<#list importEntityFrameworkPackages as pkg>\nimport ${pkg};\n</#list>\n\n<#list importEntityJavaPackages as pkg>\nimport ${pkg};\n</#list>\n\n/**\n * <p>\n * ${table.comment!}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#list entityClassAnnotations as an>\n${an.displayName}\n</#list>\n<#if superEntityClass??>\npublic class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {\n<#elseif activeRecord>\npublic class ${entity} extends Model<${entity}> {\n<#elseif entitySerialVersionUID>\npublic class ${entity} implements Serializable {\n<#else>\npublic class ${entity} {\n</#if>\n<#if entitySerialVersionUID>\n\n    <#if entitySerialAnnotation>\n    @Serial\n    </#if>\n    private static final long serialVersionUID = 1L;\n</#if>\n<#-- ----------  BEGIN 字段循环遍历  ---------->\n<#list table.fields as field>\n    <#if field.keyFlag>\n        <#assign keyPropertyName=\"${field.propertyName}\"/>\n    </#if>\n\n    <#if field.comment!?length gt 0>\n        <#if entityFieldUseJavaDoc>\n    /**\n     * ${field.comment}\n     */\n        </#if>\n    </#if>\n    <#list field.annotationAttributesList as an>\n    ${an.displayName}\n    </#list>\n    private ${field.propertyType} ${field.propertyName};\n</#list>\n<#------------  END 字段循环遍历  ---------->\n<#if !entityLombokModel>\n    <#list table.fields as field>\n        <#if field.propertyType == \"boolean\">\n            <#assign getprefix=\"is\"/>\n        <#else>\n            <#assign getprefix=\"get\"/>\n        </#if>\n\n    public ${field.propertyType} ${getprefix}${field.capitalName}() {\n        return ${field.propertyName};\n    }\n\n    <#if chainModel>\n    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n    <#else>\n    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n    </#if>\n        this.${field.propertyName} = ${field.propertyName};\n        <#if chainModel>\n        return this;\n        </#if>\n    }\n    </#list>\n</#if>\n<#if entityColumnConstant>\n    <#list table.fields as field>\n\n    public static final String ${field.name?upper_case} = \"${field.name}\";\n    </#list>\n</#if>\n<#if activeRecord>\n\n    @Override\n    public Serializable pkVal() {\n    <#if keyPropertyName??>\n        return this.${keyPropertyName};\n    <#else>\n        return null;\n    </#if>\n    }\n</#if>\n<#if !entityLombokModel && entityToString>\n\n    @Override\n    public String toString() {\n        return \"${entity}{\" +\n    <#list table.fields as field>\n        <#if field_index==0>\n            \"${field.propertyName} = \" + ${field.propertyName} +\n        <#else>\n            \", ${field.propertyName} = \" + ${field.propertyName} +\n        </#if>\n    </#list>\n            \"}\";\n    }\n</#if>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.java.vm",
    "content": "package ${package.Entity};\n\n#foreach($pkg in ${importEntityFrameworkPackages})\nimport ${pkg};\n#end\n\n#foreach($pkg in ${importEntityJavaPackages})\nimport ${pkg};\n#end\n\n/**\n * <p>\n * $!{table.comment}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#foreach($an in ${entityClassAnnotations})\n${an.displayName}\n#end\n#if(${superEntityClass})\npublic class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {\n#elseif(${activeRecord})\npublic class ${entity} extends Model<${entity}> {\n#elseif(${entitySerialVersionUID})\npublic class ${entity} implements Serializable {\n#else\npublic class ${entity} {\n#end\n#if(${entitySerialVersionUID})\n\n    #if(${entitySerialAnnotation})\n    @Serial\n    #end\n    private static final long serialVersionUID = 1L;\n#end\n## ----------  BEGIN 字段循环遍历  ----------\n#foreach($field in ${table.fields})\n\n#if(${field.keyFlag})\n#set($keyPropertyName=${field.propertyName})\n#end\n#if(\"$!field.comment\" != \"\")\n  #if(${entityFieldUseJavaDoc})\n    /**\n     * ${field.comment}\n     */\n  #end\n#end\n#foreach($an in ${field.annotationAttributesList})\n    ${an.displayName}\n#end\n    private ${field.propertyType} ${field.propertyName};\n#end\n## ----------  END 字段循环遍历  ----------\n#if(!${entityLombokModel})\n#foreach($field in ${table.fields})\n  #if(${field.propertyType.equals(\"boolean\")})\n    #set($getprefix=\"is\")\n  #else\n    #set($getprefix=\"get\")\n  #end\n\n    public ${field.propertyType} ${getprefix}${field.capitalName}() {\n        return ${field.propertyName};\n    }\n\n  #if(${chainModel})\n    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n  #else\n    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n  #end\n        this.${field.propertyName} = ${field.propertyName};\n  #if(${chainModel})\n        return this;\n  #end\n    }\n#end\n## --foreach end---\n#end\n## --end of #if(!${entityLombokModel})--\n#if(${entityColumnConstant})\n  #foreach($field in ${table.fields})\n\n    public static final String ${field.name.toUpperCase()} = \"${field.name}\";\n  #end\n#end\n#if(${activeRecord})\n\n    @Override\n    public Serializable pkVal() {\n  #if(${keyPropertyName})\n        return this.${keyPropertyName};\n  #else\n        return null;\n  #end\n    }\n#end\n#if(!${entityLombokModel} && ${entityToString})\n\n    @Override\n    public String toString() {\n        return \"${entity}{\" +\n  #foreach($field in ${table.fields})\n    #if($!{foreach.index}==0)\n            \"${field.propertyName} = \" + ${field.propertyName} +\n    #else\n            \", ${field.propertyName} = \" + ${field.propertyName} +\n    #end\n  #end\n            \"}\";\n    }\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.kt.btl",
    "content": "package ${package.Entity}\n\n<% for(pkg in importEntityFrameworkPackages){ %>\nimport ${pkg}\n<% } %>\n\n<% for(pkg in importEntityJavaPackages){ %>\nimport ${pkg}\n<% } %>\n\n/**\n * <p>\n * ${table.comment!}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<% for(an in entityClassAnnotations){ %>\n${an.displayName}\n<% } %>\n<% if(isNotEmpty(superEntityClass)){ %>\nclass ${entity} : ${superEntityClass}<% if(activeRecord){ %><${entity}><%}%>() {\n<% }else if(activeRecord){ %>\nclass ${entity} : Model<${entity}>() {\n<% }else if(entitySerialVersionUID){ %>\nclass ${entity} : Serializable {\n<% }else{ %>\nclass ${entity} {\n<% } %>\n\n<% /** -----------BEGIN 字段循环遍历----------- **/ %>\n<% for(field in table.fields){ %>\n    <%\n    if(field.keyFlag){\n        var keyPropertyName = field.propertyName;\n    }\n    %>\n    <% if(isNotEmpty(field.comment)){ %>\n        <% if(entityFieldUseJavaDoc){ %>\n    /**\n     * ${field.comment}\n     */\n        <% } %>\n    <% } %>\n    <% for(an in field.annotationAttributesList){ %>\n    ${an.displayName}\n    <% } %>\n    <% if(field.propertyType == 'Integer'){ %>\n    var ${field.propertyName}: Int? = null\n    <% }else{ %>\n    var ${field.propertyName}: ${field.propertyType}? = null\n    <% } %>\n\n<% } %>\n<% /** -----------END 字段循环遍历----------- **/ %>\n<% if(entityColumnConstant){ %>\n    companion object {\n   <% for(field in table.fields){ %>\n    const val ${strutil.toUpperCase(field.name)} : String = \"${field.name}\"\n   <% } %>\n    }\n<% } %>\n<% if(activeRecord){ %>\n    @Override\n    override fun pkVal(): Serializable? {\n    <% if(isNotEmpty(keyPropertyName)){ %>\n        return this.${keyPropertyName}\n    <% }else{ %>\n        return null;\n    <% } %>\n    }\n\n<% } %>\n<% if(entityToString){ %>\n    override fun toString(): String {\n        return \"${entity}{\" +\n    <% for(field in table.fields){ %>\n       <% if(fieldLP.dataIndex==0){ %>\n        \"${field.propertyName}=\" + ${field.propertyName} +\n       <% }else{ %>\n        \", ${field.propertyName}=\" + ${field.propertyName} +\n       <% } %>\n    <% } %>\n        \"}\"\n    }\n<% } %>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.kt.ej",
    "content": "package #(package.Entity);\n\n#for(pkg : importEntityFrameworkPackages)\nimport #(pkg)\n#end\n\n#for(pkg : importEntityJavaPackages)\nimport #(pkg)\n#end\n\n/**\n * <p>\n * #(table.comment)\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n#for(an : entityClassAnnotations)\n#(an.displayName)\n#end\n#if(superEntityClass)\nclass #(entity) : #(superEntityClass)#if(activeRecord)<#(entity)>#end() {\n#elseif(activeRecord)\nclass #(entity) : Model<#(entity)>() {\n#elseif(entitySerialVersionUID)\nclass #(entity) : Serializable {\n#else\nclass #(entity) {\n#end\n\n### ----------  BEGIN 字段循环遍历  ----------\n#for(field : table.fields)\n#if(field.isKeyFlag())\n#set(keyPropertyName = field.propertyName)\n#end\n#if(field.comment != null)\n    #if(entityFieldUseJavaDoc)\n    /**\n     * #(field.comment)\n     */\n    #end\n#end\n#for(an : field.annotationAttributesList)\n    #(an.displayName)\n#end\n    #if(field.propertyType == \"Integer\")\n    var #(field.propertyName): Int? = null\n    #else\n    var #(field.propertyName): #(field.propertyType)? = null\n    #end\n\n#end\n### ----------  END 字段循环遍历  ----------\n#if(entityColumnConstant)\n    companion object {\n#for(field : table.fields)\n\n        const val #(field.name.toUpperCase()) : String = \"#(field.name)\"\n\n#end\n    }\n\n#end\n#if(activeRecord)\n    override fun pkVal(): Serializable? {\n#if(keyPropertyName)\n        return #(keyPropertyName)\n#else\n        return null\n#end\n    }\n\n#end\n#if(entityToString)\n    override fun toString(): String {\n        return \"#(entity){\" +\n#for(field : table.fields)\n#if(for.index == 0)\n        \"#(field.propertyName)=\" + #(field.propertyName) +\n#else\n        \", #(field.propertyName)=\" + #(field.propertyName) +\n#end\n#end\n        \"}\"\n    }\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.kt.ftl",
    "content": "package ${package.Entity}\n\n<#list importEntityFrameworkPackages as pkg>\nimport ${pkg}\n</#list>\n\n<#list importEntityJavaPackages as pkg>\nimport ${pkg}\n</#list>\n\n/**\n * <p>\n * ${table.comment}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#list entityClassAnnotations as an>\n${an.displayName}\n</#list>\n<#if superEntityClass??>\nclass ${entity} : ${superEntityClass}<#if activeRecord><${entity}></#if>() {\n<#elseif activeRecord>\nclass ${entity} : Model<${entity}>() {\n<#elseif entitySerialVersionUID>\nclass ${entity} : Serializable {\n<#else>\nclass ${entity} {\n</#if>\n\n<#-- ----------  BEGIN 字段循环遍历  ---------->\n<#list table.fields as field>\n<#if field.keyFlag>\n    <#assign keyPropertyName=\"${field.propertyName}\"/>\n</#if>\n<#if field.comment!?length gt 0>\n    <#if entityFieldUseJavaDoc>\n    /**\n     * ${field.comment}\n     */\n    </#if>\n</#if>\n<#list field.annotationAttributesList as an>\n    ${an.displayName}\n</#list>\n    <#if field.propertyType == \"Integer\">\n    var ${field.propertyName}: Int? = null\n    <#else>\n    var ${field.propertyName}: ${field.propertyType}? = null\n    </#if>\n\n</#list>\n<#-- ----------  END 字段循环遍历  ---------->\n<#if entityColumnConstant>\n    companion object {\n<#list table.fields as field>\n\n        const val ${field.name?upper_case} : String = \"${field.name}\"\n\n</#list>\n    }\n\n</#if>\n<#if activeRecord>\n    override fun pkVal(): Serializable? {\n<#if keyPropertyName??>\n        return ${keyPropertyName}\n<#else>\n        return null\n</#if>\n    }\n\n</#if>\n<#if entityToString>\n    override fun toString(): String {\n        return \"${entity}{\" +\n<#list table.fields as field>\n<#if field_index==0>\n        \"${field.propertyName}=\" + ${field.propertyName} +\n<#else>\n        \", ${field.propertyName}=\" + ${field.propertyName} +\n</#if>\n</#list>\n        \"}\"\n    }\n</#if>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/entity.kt.vm",
    "content": "package ${package.Entity};\n\n#foreach($pkg in ${importEntityFrameworkPackages})\nimport ${pkg}\n#end\n\n#foreach($pkg in ${importEntityJavaPackages})\nimport ${pkg}\n#end\n\n/**\n * <p>\n * $!{table.comment}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#foreach($an in ${entityClassAnnotations})\n${an.displayName}\n#end\n#if(${superEntityClass})\nclass ${entity} : ${superEntityClass}#if(${activeRecord})<${entity}>#end() {\n#elseif(${activeRecord})\nclass ${entity} : Model<${entity}>() {\n#elseif(${entitySerialVersionUID})\nclass ${entity} : Serializable {\n#else\nclass ${entity} {\n#end\n\n## ----------  BEGIN 字段循环遍历  ----------\n#foreach($field in ${table.fields})\n#if(${field.keyFlag})\n#set($keyPropertyName=${field.propertyName})\n#end\n#if(\"$!field.comment\" != \"\")\n    #if(${entityFieldUseJavaDoc})\n    /**\n     * ${field.comment}\n     */\n    #end\n#end\n#foreach($an in ${field.annotationAttributesList})\n    ${an.displayName}\n#end\n    #if(${field.propertyType} == \"Integer\")\n    var ${field.propertyName}: Int? = null\n    #else\n    var ${field.propertyName}: ${field.propertyType}? = null\n    #end\n\n#end\n## ----------  END 字段循环遍历  ----------\n#if(${entityColumnConstant})\n    companion object {\n#foreach($field in ${table.fields})\n\n        const val ${field.name.toUpperCase()} : String = \"${field.name}\"\n\n#end\n    }\n\n#end\n#if(${activeRecord})\n    override fun pkVal(): Serializable? {\n#if(${keyPropertyName})\n        return ${keyPropertyName}\n#else\n        return null\n#end\n    }\n\n#end\n#if(${entityToString})\n    override fun toString(): String {\n        return \"${entity}{\" +\n#foreach($field in ${table.fields})\n#if($!{foreach.index}==0)\n        \"${field.propertyName}=\" + ${field.propertyName} +\n#else\n        \", ${field.propertyName}=\" + ${field.propertyName} +\n#end\n#end\n        \"}\"\n    }\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.java.btl",
    "content": "package ${package.Mapper};\n\n<% for(pkg in importMapperFrameworkPackages){ %>\nimport ${pkg};\n<% } %>\n<% if(isNotEmpty(importMapperJavaPackages)){ %>\n\n<% for(pkg in importMapperJavaPackages){ %>\nimport ${pkg};\n<% } %>\n<% } %>\n\n/**\n * <p>\n * ${table.comment!} Mapper 接口\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<% if(mapperAnnotationClass!=null){ %>\n@${mapperAnnotationClass.simpleName}\n<% } %>\n<% if(kotlin){ %>\ninterface ${table.mapperName} : ${superMapperClass}<${entity}> {\n<% }else{ %>\npublic interface ${table.mapperName} extends ${superMapperClass}<${entity}> {\n<% } %>\n\n<% for(m in mapperMethodList){ %>\n    /**\n     * generate by ${m.indexName}\n     *\n     <% for(f in m.tableFieldList) { %>\n     * @param ${f.propertyName} ${f.comment}\n     <% } %>\n     */\n    ${m.method}\n<% } %>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.java.ej",
    "content": "package #(package.Mapper);\n\n#for(pkg : importMapperFrameworkPackages)\nimport #(pkg);\n#end\n#if(!importMapperJavaPackages.isEmpty())\n\n#for(pkg : importMapperJavaPackages)\nimport #(pkg);\n#end\n#end\n\n/**\n * <p>\n * #(table.comment) Mapper 接口\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n#if(mapperAnnotationClass)\n@#(mapperAnnotationClass.simpleName)\n#end\n#if(kotlin)\ninterface #(table.mapperName) : #(superMapperClass)<#(entity)> {\n#else\npublic interface #(table.mapperName) extends #(superMapperClass)<#(entity)> {\n#end\n\n#for(m : mapperMethodList)\n    /**\n     * generate by #(m.indexName)\n     *\n     #for(f : m.tableFieldList)\n     * @param #(f.propertyName) #(f.comment)\n     #end\n     */\n    #(m.method)\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.java.ftl",
    "content": "package ${package.Mapper};\n\n<#list importMapperFrameworkPackages as pkg>\nimport ${pkg};\n</#list>\n<#if importMapperJavaPackages?size !=0>\n\n  <#list importMapperJavaPackages as pkg>\nimport ${pkg};\n   </#list>\n</#if>\n\n/**\n * <p>\n * ${table.comment!} Mapper 接口\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#if mapperAnnotationClass??>\n@${mapperAnnotationClass.simpleName}\n</#if>\n<#if kotlin>\ninterface ${table.mapperName} : ${superMapperClass}<${entity}> {\n<#else>\npublic interface ${table.mapperName} extends ${superMapperClass}<${entity}> {\n</#if>\n\n<#list mapperMethodList as m>\n    /**\n     * generate by ${m.indexName}\n     *\n    <#list m.tableFieldList as f>\n     * @param ${f.propertyName} ${f.comment}\n    </#list>\n     */\n    ${m.method}\n</#list>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.java.vm",
    "content": "package ${package.Mapper};\n\n#foreach($pkg in ${importMapperFrameworkPackages})\nimport ${pkg};\n#end\n#if($importMapperJavaPackages.size()>0)\n\n#foreach($pkg in ${importMapperJavaPackages})\nimport ${pkg};\n#end\n#end\n\n/**\n * <p>\n * $!{table.comment} Mapper 接口\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#if(${mapperAnnotationClass})\n@${mapperAnnotationClass.simpleName}\n#end\n#if(${kotlin})\ninterface ${table.mapperName} : ${superMapperClass}<${entity}> {\n#else\npublic interface ${table.mapperName} extends ${superMapperClass}<${entity}> {\n#end\n\n#foreach($m in ${mapperMethodList})\n    /**\n     * generate by ${m.indexName}\n     *\n     #foreach($f in ${m.tableFieldList})\n     * @param ${f.propertyName} ${f.comment}\n     #end\n     */\n    ${m.method}\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.xml.btl",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${package.Mapper}.${table.mapperName}\">\n\n<% if(enableCache){ %>\n    <!-- 开启二级缓存 -->\n    <cache type=\"${cacheClassName}\"/>\n\n<% } %>\n<% if(baseResultMap){ %>\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"${package.Entity}.${entity}\">\n<% for(field in table.fields){ %>\n   <% /** 生成主键排在第一位 **/ %>\n   <% if(field.keyFlag){ %>\n        <id column=\"${field.name}\" property=\"${field.propertyName}\" />\n   <% } %>\n<% } %>\n<% for(field in table.commonFields){ %>\n    <% /** 生成公共字段 **/ %>\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n<% } %>\n<% for(field in table.fields){ %>\n   <% /** 生成普通字段 **/ %>\n   <% if(!field.keyFlag){ %>\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n   <% } %>\n<% } %>\n    </resultMap>\n<% } %>\n<% if(baseColumnList){ %>\n    <!-- 通用查询结果列 -->\n    <sql id=\"Base_Column_List\">\n<% for(field in table.commonFields){ %>\n        ${field.columnName},\n<% } %>\n        ${table.fieldNames}\n    </sql>\n\n<% } %>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.xml.ej",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"#(package.Mapper).#(table.mapperName)\">\n\n#if(enableCache)\n    <!-- 开启二级缓存 -->\n    <cache type=\"#(cacheClassName)\"/>\n\n#end\n#if(baseResultMap)\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"#(package.Entity).#(entity)\">\n#for(field : table.fields)\n#if(field.isKeyFlag())###生成主键排在第一位\n        <id column=\"#(field.name)\" property=\"#(field.propertyName)\" />\n#end\n#end\n#for(field : table.commonFields)###生成公共字段\n        <result column=\"#(field.name)\" property=\"#(field.propertyName)\" />\n#end\n#for(field : table.fields)\n#if(!field.isKeyFlag())###生成普通字段\n        <result column=\"#(field.name)\" property=\"#(field.propertyName)\" />\n#end\n#end\n    </resultMap>\n\n#end\n#if(baseColumnList)\n    <!-- 通用查询结果列 -->\n    <sql id=\"Base_Column_List\">\n#for(field : table.commonFields)\n        #(field.columnName),\n#end\n        #(table.fieldNames)\n    </sql>\n\n#end\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.xml.ftl",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${package.Mapper}.${table.mapperName}\">\n\n<#if enableCache>\n    <!-- 开启二级缓存 -->\n    <cache type=\"${cacheClassName}\"/>\n\n</#if>\n<#if baseResultMap>\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"${package.Entity}.${entity}\">\n<#list table.fields as field>\n<#if field.keyFlag><#--生成主键排在第一位-->\n        <id column=\"${field.name}\" property=\"${field.propertyName}\" />\n</#if>\n</#list>\n<#list table.commonFields as field><#--生成公共字段 -->\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n</#list>\n<#list table.fields as field>\n<#if !field.keyFlag><#--生成普通字段 -->\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n</#if>\n</#list>\n    </resultMap>\n\n</#if>\n<#if baseColumnList>\n    <!-- 通用查询结果列 -->\n    <sql id=\"Base_Column_List\">\n<#list table.commonFields as field>\n        ${field.columnName},\n</#list>\n        ${table.fieldNames}\n    </sql>\n\n</#if>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/mapper.xml.vm",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"${package.Mapper}.${table.mapperName}\">\n\n#if(${enableCache})\n    <!-- 开启二级缓存 -->\n    <cache type=\"${cacheClassName}\"/>\n\n#end\n#if(${baseResultMap})\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"${package.Entity}.${entity}\">\n#foreach($field in ${table.fields})\n#if(${field.keyFlag})##生成主键排在第一位\n        <id column=\"${field.name}\" property=\"${field.propertyName}\" />\n#end\n#end\n#foreach($field in ${table.commonFields})##生成公共字段\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n#end\n#foreach($field in ${table.fields})\n#if(!${field.keyFlag})##生成普通字段\n        <result column=\"${field.name}\" property=\"${field.propertyName}\" />\n#end\n#end\n    </resultMap>\n\n#end\n#if(${baseColumnList})\n    <!-- 通用查询结果列 -->\n    <sql id=\"Base_Column_List\">\n#foreach($field in ${table.commonFields})\n        ${field.columnName},\n#end\n        ${table.fieldNames}\n    </sql>\n\n#end\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/service.java.btl",
    "content": "package ${package.Service};\n\nimport ${package.Entity}.${entity};\nimport ${superServiceClassPackage};\n\n/**\n * <p>\n * ${table.comment!} 服务类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<% if(kotlin){ %>\ninterface ${table.serviceName} : ${superServiceClass}<${entity}>\n<% }else{ %>\npublic interface ${table.serviceName} extends ${superServiceClass}<${entity}> {\n\n}\n<% } %>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/service.java.ej",
    "content": "package #(package.Service);\n\nimport #(package.Entity).#(entity);\nimport #(superServiceClassPackage);\n\n/**\n * <p>\n * #(table.comment) 服务类\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n#if(kotlin)\ninterface #(table.serviceName) : #(superServiceClass)<#(entity)>\n#else\npublic interface #(table.serviceName) extends #(superServiceClass)<#(entity)> {\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/service.java.ftl",
    "content": "package ${package.Service};\n\nimport ${package.Entity}.${entity};\nimport ${superServiceClassPackage};\n\n/**\n * <p>\n * ${table.comment!} 服务类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#if kotlin>\ninterface ${table.serviceName} : ${superServiceClass}<${entity}>\n<#else>\npublic interface ${table.serviceName} extends ${superServiceClass}<${entity}> {\n\n}\n</#if>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/service.java.vm",
    "content": "package ${package.Service};\n\nimport ${package.Entity}.${entity};\nimport ${superServiceClassPackage};\n\n/**\n * <p>\n * $!{table.comment} 服务类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#if(${kotlin})\ninterface ${table.serviceName} : ${superServiceClass}<${entity}>\n#else\npublic interface ${table.serviceName} extends ${superServiceClass}<${entity}> {\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/serviceImpl.java.btl",
    "content": "package ${package.ServiceImpl};\n\nimport ${package.Entity}.${entity};\nimport ${package.Mapper}.${table.mapperName};\n<% if(generateService){ %>\nimport ${package.Service}.${table.serviceName};\n<% } %>\nimport ${superServiceImplClassPackage};\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * ${table.comment!} 服务实现类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n@Service\n<% if(kotlin){ %>\nopen class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>()<% if(generateService){ %>, ${table.serviceName}<% } %> {\n\n}\n<% }else{ %>\npublic class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}><% if(generateService){ %> implements ${table.serviceName}<% } %> {\n\n}\n<% } %>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/serviceImpl.java.ej",
    "content": "package #(package.ServiceImpl);\n\nimport #(package.Entity).#(entity);\nimport #(package.Mapper).#(table.mapperName);\n#if(generateService)\nimport #(package.Service).#(table.serviceName);\n#end\nimport #(superServiceImplClassPackage);\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * #(table.comment) 服务实现类\n * </p>\n *\n * @author #(author)\n * @since #(date)\n */\n@Service\n#if(kotlin)\nopen class #(table.serviceImplName) : #(superServiceImplClass)<#(table.mapperName), #(entity)>()#if(generateService), #(table.serviceName) #end{\n\n}\n#else\npublic class #(table.serviceImplName) extends #(superServiceImplClass)<#(table.mapperName), #(entity)> #if(generateService)implements #(table.serviceName) #end{\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/serviceImpl.java.ftl",
    "content": "package ${package.ServiceImpl};\n\nimport ${package.Entity}.${entity};\nimport ${package.Mapper}.${table.mapperName};\n<#if generateService>\nimport ${package.Service}.${table.serviceName};\n</#if>\nimport ${superServiceImplClassPackage};\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * ${table.comment!} 服务实现类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n@Service\n<#if kotlin>\nopen class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>()<#if generateService>, ${table.serviceName}</#if> {\n\n}\n<#else>\npublic class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}><#if generateService> implements ${table.serviceName}</#if> {\n\n}\n</#if>\n"
  },
  {
    "path": "mybatis-plus-generator/src/main/resources/templates/serviceImpl.java.vm",
    "content": "package ${package.ServiceImpl};\n\nimport ${package.Entity}.${entity};\nimport ${package.Mapper}.${table.mapperName};\n#if(${generateService})\nimport ${package.Service}.${table.serviceName};\n#end\nimport ${superServiceImplClassPackage};\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * $!{table.comment} 服务实现类\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n@Service\n#if(${kotlin})\nopen class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>()#if(${generateService}), ${table.serviceName}#end {\n\n}\n#else\npublic class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}>#if(${generateService}) implements ${table.serviceName}#end {\n\n}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/controller/SimpleController.java",
    "content": "package com.baomidou.demo.beetl.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/demo.beetl/simple\")\npublic class SimpleController {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/entity/Simple.java",
    "content": "package com.baomidou.demo.beetl.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\npublic class Simple implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    private Integer id;\n\n    /**\n     * 姓名\n     */\n    private String name;\n\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n    /**\n     * 删除标识1\n     */\n    private Byte deleteFlag;\n\n    /**\n     * 删除标识2\n     */\n    private Byte deleted;\n\n    /**\n     * 测试布尔类型\n     */\n    private Byte isOk;\n\n    /**\n     * 版本\n     */\n    private Long version;\n\n    /**\n     * 创建时间\n     */\n    private LocalDateTime createTime;\n\n    /**\n     * 更新时间\n     */\n    private LocalDateTime updateTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Byte getDeleteFlag() {\n        return deleteFlag;\n    }\n\n    public void setDeleteFlag(Byte deleteFlag) {\n        this.deleteFlag = deleteFlag;\n    }\n\n    public Byte getDeleted() {\n        return deleted;\n    }\n\n    public void setDeleted(Byte deleted) {\n        this.deleted = deleted;\n    }\n\n    public Byte getIsOk() {\n        return isOk;\n    }\n\n    public void setIsOk(Byte isOk) {\n        this.isOk = isOk;\n    }\n\n    public Long getVersion() {\n        return version;\n    }\n\n    public void setVersion(Long version) {\n        this.version = version;\n    }\n\n    public LocalDateTime getCreateTime() {\n        return createTime;\n    }\n\n    public void setCreateTime(LocalDateTime createTime) {\n        this.createTime = createTime;\n    }\n\n    public LocalDateTime getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(LocalDateTime updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"Simple{\" +\n            \"id = \" + id +\n            \", name = \" + name +\n            \", age = \" + age +\n            \", deleteFlag = \" + deleteFlag +\n            \", deleted = \" + deleted +\n            \", isOk = \" + isOk +\n            \", version = \" + version +\n            \", createTime = \" + createTime +\n            \", updateTime = \" + updateTime +\n            \"}\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/mapper/SimpleMapper.java",
    "content": "package com.baomidou.demo.beetl.mapper;\n\nimport com.baomidou.demo.beetl.entity.Simple;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface SimpleMapper extends BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.demo.beetl.mapper.SimpleMapper\">\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/service/ISimpleService.java",
    "content": "package com.baomidou.demo.beetl.service;\n\nimport com.baomidou.demo.beetl.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface ISimpleService extends IService<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/beetl/service/impl/SimpleServiceImpl.java",
    "content": "package com.baomidou.demo.beetl.service.impl;\n\nimport com.baomidou.demo.beetl.entity.Simple;\nimport com.baomidou.demo.beetl.mapper.SimpleMapper;\nimport com.baomidou.demo.beetl.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\npublic class SimpleServiceImpl extends ServiceImpl<SimpleMapper, Simple> implements ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/controller/SimpleController.java",
    "content": "package com.baomidou.demo.enjoy.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/demo.enjoy/simple\")\npublic class SimpleController {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/entity/Simple.java",
    "content": "package com.baomidou.demo.enjoy.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\npublic class Simple implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    private Integer id;\n\n    /**\n     * 姓名\n     */\n    private String name;\n\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n    /**\n     * 删除标识1\n     */\n    private Byte deleteFlag;\n\n    /**\n     * 删除标识2\n     */\n    private Byte deleted;\n\n    /**\n     * 测试布尔类型\n     */\n    private Byte isOk;\n\n    /**\n     * 版本\n     */\n    private Long version;\n\n    /**\n     * 创建时间\n     */\n    private LocalDateTime createTime;\n\n    /**\n     * 更新时间\n     */\n    private LocalDateTime updateTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Byte getDeleteFlag() {\n        return deleteFlag;\n    }\n\n    public void setDeleteFlag(Byte deleteFlag) {\n        this.deleteFlag = deleteFlag;\n    }\n\n    public Byte getDeleted() {\n        return deleted;\n    }\n\n    public void setDeleted(Byte deleted) {\n        this.deleted = deleted;\n    }\n\n    public Byte getIsOk() {\n        return isOk;\n    }\n\n    public void setIsOk(Byte isOk) {\n        this.isOk = isOk;\n    }\n\n    public Long getVersion() {\n        return version;\n    }\n\n    public void setVersion(Long version) {\n        this.version = version;\n    }\n\n    public LocalDateTime getCreateTime() {\n        return createTime;\n    }\n\n    public void setCreateTime(LocalDateTime createTime) {\n        this.createTime = createTime;\n    }\n\n    public LocalDateTime getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(LocalDateTime updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"Simple{\" +\n            \"id = \" + id +\n            \", name = \" + name +\n            \", age = \" + age +\n            \", deleteFlag = \" + deleteFlag +\n            \", deleted = \" + deleted +\n            \", isOk = \" + isOk +\n            \", version = \" + version +\n            \", createTime = \" + createTime +\n            \", updateTime = \" + updateTime +\n            \"}\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/mapper/SimpleMapper.java",
    "content": "package com.baomidou.demo.enjoy.mapper;\n\nimport com.baomidou.demo.enjoy.entity.Simple;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface SimpleMapper extends BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.demo.enjoy.mapper.SimpleMapper\">\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/service/ISimpleService.java",
    "content": "package com.baomidou.demo.enjoy.service;\n\nimport com.baomidou.demo.enjoy.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface ISimpleService extends IService<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/enjoy/service/impl/SimpleServiceImpl.java",
    "content": "package com.baomidou.demo.enjoy.service.impl;\n\nimport com.baomidou.demo.enjoy.entity.Simple;\nimport com.baomidou.demo.enjoy.mapper.SimpleMapper;\nimport com.baomidou.demo.enjoy.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\npublic class SimpleServiceImpl extends ServiceImpl<SimpleMapper, Simple> implements ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/controller/SimpleController.java",
    "content": "package com.baomidou.demo.freemarker.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/demo.freemarker/simple\")\npublic class SimpleController {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/entity/Simple.java",
    "content": "package com.baomidou.demo.freemarker.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\npublic class Simple implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    private Integer id;\n\n    /**\n     * 姓名\n     */\n    private String name;\n\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n    /**\n     * 删除标识1\n     */\n    private Byte deleteFlag;\n\n    /**\n     * 删除标识2\n     */\n    private Byte deleted;\n\n    /**\n     * 测试布尔类型\n     */\n    private Byte isOk;\n\n    /**\n     * 版本\n     */\n    private Long version;\n\n    /**\n     * 创建时间\n     */\n    private LocalDateTime createTime;\n\n    /**\n     * 更新时间\n     */\n    private LocalDateTime updateTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Byte getDeleteFlag() {\n        return deleteFlag;\n    }\n\n    public void setDeleteFlag(Byte deleteFlag) {\n        this.deleteFlag = deleteFlag;\n    }\n\n    public Byte getDeleted() {\n        return deleted;\n    }\n\n    public void setDeleted(Byte deleted) {\n        this.deleted = deleted;\n    }\n\n    public Byte getIsOk() {\n        return isOk;\n    }\n\n    public void setIsOk(Byte isOk) {\n        this.isOk = isOk;\n    }\n\n    public Long getVersion() {\n        return version;\n    }\n\n    public void setVersion(Long version) {\n        this.version = version;\n    }\n\n    public LocalDateTime getCreateTime() {\n        return createTime;\n    }\n\n    public void setCreateTime(LocalDateTime createTime) {\n        this.createTime = createTime;\n    }\n\n    public LocalDateTime getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(LocalDateTime updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"Simple{\" +\n            \"id = \" + id +\n            \", name = \" + name +\n            \", age = \" + age +\n            \", deleteFlag = \" + deleteFlag +\n            \", deleted = \" + deleted +\n            \", isOk = \" + isOk +\n            \", version = \" + version +\n            \", createTime = \" + createTime +\n            \", updateTime = \" + updateTime +\n            \"}\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/mapper/SimpleMapper.java",
    "content": "package com.baomidou.demo.freemarker.mapper;\n\nimport com.baomidou.demo.freemarker.entity.Simple;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface SimpleMapper extends BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.demo.freemarker.mapper.SimpleMapper\">\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/service/ISimpleService.java",
    "content": "package com.baomidou.demo.freemarker.service;\n\nimport com.baomidou.demo.freemarker.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface ISimpleService extends IService<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/freemarker/service/impl/SimpleServiceImpl.java",
    "content": "package com.baomidou.demo.freemarker.service.impl;\n\nimport com.baomidou.demo.freemarker.entity.Simple;\nimport com.baomidou.demo.freemarker.mapper.SimpleMapper;\nimport com.baomidou.demo.freemarker.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\npublic class SimpleServiceImpl extends ServiceImpl<SimpleMapper, Simple> implements ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/controller/SimpleController.java",
    "content": "package com.baomidou.demo.velocity.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/demo.velocity/simple\")\npublic class SimpleController {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/entity/Simple.java",
    "content": "package com.baomidou.demo.velocity.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\npublic class Simple implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    private Integer id;\n\n    /**\n     * 姓名\n     */\n    private String name;\n\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n    /**\n     * 删除标识1\n     */\n    private Byte deleteFlag;\n\n    /**\n     * 删除标识2\n     */\n    private Byte deleted;\n\n    /**\n     * 测试布尔类型\n     */\n    private Byte isOk;\n\n    /**\n     * 版本\n     */\n    private Long version;\n\n    /**\n     * 创建时间\n     */\n    private LocalDateTime createTime;\n\n    /**\n     * 更新时间\n     */\n    private LocalDateTime updateTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public void setId(Integer id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public Byte getDeleteFlag() {\n        return deleteFlag;\n    }\n\n    public void setDeleteFlag(Byte deleteFlag) {\n        this.deleteFlag = deleteFlag;\n    }\n\n    public Byte getDeleted() {\n        return deleted;\n    }\n\n    public void setDeleted(Byte deleted) {\n        this.deleted = deleted;\n    }\n\n    public Byte getIsOk() {\n        return isOk;\n    }\n\n    public void setIsOk(Byte isOk) {\n        this.isOk = isOk;\n    }\n\n    public Long getVersion() {\n        return version;\n    }\n\n    public void setVersion(Long version) {\n        this.version = version;\n    }\n\n    public LocalDateTime getCreateTime() {\n        return createTime;\n    }\n\n    public void setCreateTime(LocalDateTime createTime) {\n        this.createTime = createTime;\n    }\n\n    public LocalDateTime getUpdateTime() {\n        return updateTime;\n    }\n\n    public void setUpdateTime(LocalDateTime updateTime) {\n        this.updateTime = updateTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"Simple{\" +\n            \"id = \" + id +\n            \", name = \" + name +\n            \", age = \" + age +\n            \", deleteFlag = \" + deleteFlag +\n            \", deleted = \" + deleted +\n            \", isOk = \" + isOk +\n            \", version = \" + version +\n            \", createTime = \" + createTime +\n            \", updateTime = \" + updateTime +\n            \"}\";\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/mapper/SimpleMapper.java",
    "content": "package com.baomidou.demo.velocity.mapper;\n\nimport com.baomidou.demo.velocity.entity.Simple;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface SimpleMapper extends BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.demo.velocity.mapper.SimpleMapper\">\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/service/ISimpleService.java",
    "content": "package com.baomidou.demo.velocity.service;\n\nimport com.baomidou.demo.velocity.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\npublic interface ISimpleService extends IService<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/demo/velocity/service/impl/SimpleServiceImpl.java",
    "content": "package com.baomidou.demo.velocity.service.impl;\n\nimport com.baomidou.demo.velocity.entity.Simple;\nimport com.baomidou.demo.velocity.mapper.SimpleMapper;\nimport com.baomidou.demo.velocity.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\npublic class SimpleServiceImpl extends ServiceImpl<SimpleMapper, Simple> implements ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/DataSourceConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;\nimport com.baomidou.mybatisplus.generator.config.querys.H2Query;\nimport com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;\nimport com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;\nimport org.apache.ibatis.datasource.unpooled.UnpooledDataSource;\nimport org.h2.Driver;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n\n/**\n * @author nieqiurong 2020/10/10.\n */\npublic class DataSourceConfigTest {\n\n    @Test\n    void buildTest() {\n        DataSourceConfig dataSourceConfig;\n        dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\").build();\n        Assertions.assertNotNull(dataSourceConfig.getDbType());\n        Assertions.assertNotNull(dataSourceConfig.getConn());\n        Assertions.assertNotNull(dataSourceConfig.getTypeConvert());\n        Assertions.assertEquals(DbType.H2, dataSourceConfig.getDbType());\n        Assertions.assertEquals(H2Query.class, dataSourceConfig.getDbQuery().getClass());\n\n        dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\")\n            .dbQuery(new MySqlQuery()).schema(\"mp\").keyWordsHandler(new MySqlKeyWordsHandler()).typeConvert(new PostgreSqlTypeConvert())\n            .build();\n        Assertions.assertEquals(\"mp\", dataSourceConfig.getSchemaName());\n        Assertions.assertEquals(DbType.H2, dataSourceConfig.getDbType());\n        Assertions.assertEquals(MySqlQuery.class, dataSourceConfig.getDbQuery().getClass());\n        Assertions.assertNotNull(dataSourceConfig.getKeyWordsHandler());\n        Assertions.assertEquals(MySqlKeyWordsHandler.class, dataSourceConfig.getKeyWordsHandler().getClass());\n        Assertions.assertEquals(PostgreSqlTypeConvert.class, dataSourceConfig.getTypeConvert().getClass());\n\n        dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\")\n            .dbQuery(new MySqlQuery()).schema(\"mp\").keyWordsHandler(new MySqlKeyWordsHandler()).typeConvert(new PostgreSqlTypeConvert())\n            .driverClassName(\"org.h2.Driver\")\n            .build();\n        Assertions.assertEquals(\"mp\", dataSourceConfig.getSchemaName());\n        Assertions.assertEquals(DbType.H2, dataSourceConfig.getDbType());\n        Assertions.assertEquals(MySqlQuery.class, dataSourceConfig.getDbQuery().getClass());\n        Assertions.assertNotNull(dataSourceConfig.getKeyWordsHandler());\n        Assertions.assertEquals(\"org.h2.Driver\", dataSourceConfig.getDriverClassName());\n        Assertions.assertEquals(MySqlKeyWordsHandler.class, dataSourceConfig.getKeyWordsHandler().getClass());\n        Assertions.assertEquals(PostgreSqlTypeConvert.class, dataSourceConfig.getTypeConvert().getClass());\n    }\n\n    @Test\n    void dataSourceTest() {\n        UnpooledDataSource dataSource = new UnpooledDataSource(Driver.class.getName(), \"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\");\n        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(dataSource).build();\n        Assertions.assertNotNull(dataSourceConfig.getConn());\n        Assertions.assertNotNull(dataSourceConfig.getDbType());\n        Assertions.assertNotNull(dataSourceConfig.getTypeConvert());\n        Assertions.assertEquals(DbType.H2, dataSourceConfig.getDbType());\n        Assertions.assertEquals(H2Query.class, dataSourceConfig.getDbQuery().getClass());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/DbQueryRegistryTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.generator.config.querys.ClickHouseQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.DB2Query;\nimport com.baomidou.mybatisplus.generator.config.querys.DMQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.DbQueryRegistry;\nimport com.baomidou.mybatisplus.generator.config.querys.FirebirdQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.GaussDBSqlQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.ZenithQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.GbaseQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.H2Query;\nimport com.baomidou.mybatisplus.generator.config.querys.KingbaseESQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.MariadbQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.OracleQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.OscarQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.PostgreSqlQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.SqlServerQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.SqliteQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.SybaseQuery;\nimport com.baomidou.mybatisplus.generator.config.querys.XuguQuery;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong\n */\npublic class DbQueryRegistryTest {\n\n    @Test\n    void test() {\n        DbQueryRegistry dbQueryRegistry = new DbQueryRegistry();\n        dbQueryRegistry.getDbQuery(DbType.GAUSS_DB);\n        Assertions.assertInstanceOf(OracleQuery.class, dbQueryRegistry.getDbQuery(DbType.ORACLE));\n        Assertions.assertInstanceOf(SqlServerQuery.class, dbQueryRegistry.getDbQuery(DbType.SQL_SERVER));\n        Assertions.assertInstanceOf(PostgreSqlQuery.class, dbQueryRegistry.getDbQuery(DbType.POSTGRE_SQL));\n        Assertions.assertInstanceOf(DB2Query.class, dbQueryRegistry.getDbQuery(DbType.DB2));\n        Assertions.assertInstanceOf(MariadbQuery.class, dbQueryRegistry.getDbQuery(DbType.MARIADB));\n        Assertions.assertInstanceOf(H2Query.class, dbQueryRegistry.getDbQuery(DbType.H2));\n        Assertions.assertInstanceOf(H2Query.class, dbQueryRegistry.getDbQuery(DbType.LEALONE));\n        Assertions.assertInstanceOf(SqliteQuery.class, dbQueryRegistry.getDbQuery(DbType.SQLITE));\n        Assertions.assertInstanceOf(DMQuery.class, dbQueryRegistry.getDbQuery(DbType.DM));\n        Assertions.assertInstanceOf(KingbaseESQuery.class, dbQueryRegistry.getDbQuery(DbType.KINGBASE_ES));\n        Assertions.assertInstanceOf(MySqlQuery.class, dbQueryRegistry.getDbQuery(DbType.MYSQL));\n        Assertions.assertInstanceOf(ZenithQuery.class, dbQueryRegistry.getDbQuery(DbType.GAUSS));\n        Assertions.assertInstanceOf(GaussDBSqlQuery.class, dbQueryRegistry.getDbQuery(DbType.GAUSS_DB));\n        Assertions.assertInstanceOf(OscarQuery.class, dbQueryRegistry.getDbQuery(DbType.OSCAR));\n        Assertions.assertInstanceOf(FirebirdQuery.class, dbQueryRegistry.getDbQuery(DbType.FIREBIRD));\n        Assertions.assertInstanceOf(XuguQuery.class, dbQueryRegistry.getDbQuery(DbType.XU_GU));\n        Assertions.assertInstanceOf(ClickHouseQuery.class, dbQueryRegistry.getDbQuery(DbType.CLICK_HOUSE));\n        Assertions.assertInstanceOf(GbaseQuery.class, dbQueryRegistry.getDbQuery(DbType.GBASE));\n        Assertions.assertInstanceOf(SybaseQuery.class, dbQueryRegistry.getDbQuery(DbType.SYBASE));\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/GlobalConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\n\n/**\n * @author nieqiurong 2020/10/12.\n */\npublic class GlobalConfigTest {\n\n    private void buildAssert(GlobalConfig globalConfig) {\n        Assertions.assertTrue(globalConfig.isKotlin());\n        Assertions.assertTrue(globalConfig.isSwagger());\n        Assertions.assertTrue(globalConfig.isOpen());\n        Assertions.assertEquals(\"mp\", globalConfig.getAuthor());\n        Assertions.assertEquals(\"/temp/code\", globalConfig.getOutputDir());\n        Assertions.assertEquals(DateType.SQL_PACK, globalConfig.getDateType());\n        Assertions.assertTrue(globalConfig.isServiceInterface());\n    }\n\n    @Test\n    void builderTest() {\n        GlobalConfig globalConfig = GeneratorBuilder.globalConfigBuilder().author(\"mp\").enableKotlin()\n            .enableSwagger().dateType(DateType.SQL_PACK).outputDir(\"/temp/code\").build();\n        buildAssert(globalConfig);\n    }\n\n    @Test\n    void commentDateTest() {\n        String defaultDate = GeneratorBuilder.globalConfig().getCommentDate();\n        String commentDate = GeneratorBuilder.globalConfigBuilder().commentDate(\"yyyy-MM-dd\").build().getCommentDate();\n        Assertions.assertEquals(defaultDate, commentDate);\n        Assertions.assertEquals(\"2200年11月10日\", GeneratorBuilder.globalConfigBuilder().commentDate(() -> \"2200年11月10日\").build().getCommentDate());\n        Assertions.assertEquals(LocalDate.now().format(DateTimeFormatter.ISO_DATE), GeneratorBuilder.globalConfigBuilder().commentDate(() -> LocalDate.now().format(DateTimeFormatter.ISO_DATE)).build().getCommentDate());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/InjectionConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author lanjerry\n * @since 2021-09-06\n */\npublic class InjectionConfigTest {\n\n    @Test\n    void builderTest() {\n        Map<String, Object> customMap = new HashMap<>();\n        customMap.put(\"test\", \"baomidou\");\n        Map<String, String> customFile = new HashMap<>();\n        customFile.put(\"test.txt\", \"/templates/test.vm\");\n        InjectionConfig injectionConfig = GeneratorBuilder.injectionConfigBuilder().customMap(customMap).customFile(customFile).build();\n        Assertions.assertEquals(1, injectionConfig.getCustomMap().size());\n        Assertions.assertEquals(\"baomidou\",injectionConfig.getCustomMap().get(\"test\"));\n        Assertions.assertEquals(1, injectionConfig.getCustomFiles().size());\n        Assertions.assertEquals(\"/templates/test.vm\",injectionConfig.getCustomFiles().getFirst().getTemplatePath());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/PackageConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.CustomFile;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2020/10/6.\n */\npublic class PackageConfigTest {\n\n    @Test\n    void joinPackageTest() {\n        Assertions.assertEquals(\"com.baomidou.demo\", GeneratorBuilder.packageConfigBuilder().joinPackage(\"demo\"));\n        Assertions.assertEquals(\"com.baomidou.mp.demo\", GeneratorBuilder.packageConfigBuilder().moduleName(\"mp\").joinPackage(\"demo\"));\n        Assertions.assertEquals(\"com.baomihua.demo\", GeneratorBuilder.packageConfigBuilder().parent(\"com.baomihua\").joinPackage(\"demo\"));\n        Assertions.assertEquals(\"com.baomihua.mp.demo\", GeneratorBuilder.packageConfigBuilder().parent(\"com.baomihua\").moduleName(\"mp\").joinPackage(\"demo\"));\n        Assertions.assertEquals(\"com.baomihua.mp\", GeneratorBuilder.packageConfigBuilder().parent(\"com.baomihua\").moduleName(\"mp\").joinPackage(null));\n        Assertions.assertEquals(\"com.baomihua.mp\", GeneratorBuilder.packageConfigBuilder().parent(\"com.baomihua\").moduleName(\"mp\").joinPackage(\"\"));\n    }\n\n    private void buildAssert(PackageConfig packageConfig){\n        Assertions.assertEquals(\"com.baomihua.demo\", packageConfig.getParent());\n        Assertions.assertEquals(\"demo\", packageConfig.getModuleName());\n        Assertions.assertEquals(\"action\", packageConfig.getController());\n        Assertions.assertEquals(\"entity\", packageConfig.getEntity());\n        Assertions.assertEquals(\"dao\", packageConfig.getMapper());\n        Assertions.assertEquals(\"iservice\", packageConfig.getService());\n        Assertions.assertEquals(\"serviceIm\", packageConfig.getServiceImpl());\n        Assertions.assertEquals(1,packageConfig.getPathInfo().size());\n        Assertions.assertTrue(packageConfig.getPathInfo().containsKey(OutputFile.controller));\n    }\n\n    @Test\n    void buildTest(){\n        buildAssert(GeneratorBuilder.packageConfigBuilder().parent(\"com.baomihua\")\n            .moduleName(\"demo\").controller(\"action\").entity(\"entity\")\n            .mapper(\"dao\").service(\"iservice\").serviceImpl(\"serviceIm\")\n            .pathInfo(Collections.singletonMap(OutputFile.controller,\"bbbb\")).build());\n    }\n\n    @Test\n    void testCustomFile() {\n        var packageConfig = GeneratorBuilder.packageConfigBuilder().build();\n        var injectionConfig = GeneratorBuilder.injectionConfigBuilder()\n            .customFile(new CustomFile.Builder()\n                .fileName(\"Dto.java\").packageName(\"dto\").templatePath(\"dto.vm\")\n                .build())\n            .customFile(new CustomFile.Builder()\n                .fileName(\"Vo.java\").templatePath(\"vo.vm\")\n                .build())\n            .customFile(new CustomFile.Builder()\n                .fileName(\"Bo.java\").packageName(\"com.baomidou.bo\").templatePath(\"Bo.vm\")\n                .build())\n            .build();\n        Map<String, String> packageInfo = packageConfig.getPackageInfo(injectionConfig);\n        Assertions.assertNotNull(packageInfo.get(\"Dto\"));\n        Assertions.assertEquals(\"com.baomidou.dto\", packageInfo.get(\"Dto\"));\n        Assertions.assertNull(packageInfo.get(\"Vo\"));\n        Assertions.assertNotNull(packageInfo.get(\"Bo\"));\n        Assertions.assertEquals(\"com.baomidou.bo\", packageInfo.get(\"Bo\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/StrategyConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.generator.IFill;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.TableField;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfoTest;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport com.baomidou.mybatisplus.generator.fill.Column;\nimport com.baomidou.mybatisplus.generator.entity.BaseEntity;\nimport com.baomidou.mybatisplus.generator.entity.SuperEntity;\nimport lombok.Data;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * <p>\n * 策略测试\n * </p>\n *\n * @author hubin\n * @since 2019-02-20\n */\nclass StrategyConfigTest {\n\n    @Test\n    void test() {\n        StrategyConfig strategyConfig;\n        // 默认全开\n        strategyConfig = new StrategyConfig.Builder().build();\n        Assertions.assertTrue(strategyConfig.controller().isGenerate());\n        Assertions.assertTrue(strategyConfig.entity().isGenerate());\n        Assertions.assertTrue(strategyConfig.service().isGenerateService());\n        Assertions.assertTrue(strategyConfig.service().isGenerateServiceImpl());\n        Assertions.assertTrue(strategyConfig.mapper().isGenerateMapper());\n        Assertions.assertTrue(strategyConfig.mapper().isGenerateMapperXml());\n        strategyConfig =\n            new StrategyConfig.Builder()\n                .entityBuilder()\n                .javaTemplate(\"/templates/entity.java\")\n                .disable()\n                .serviceBuilder().disable()\n                .disableService().serviceTemplate(\"/templates/service.java\").serviceImplTemplate(\"/templates/serviceImpl.java\")\n                .mapperBuilder().disableMapper().disableMapperXml()\n                .controllerBuilder().disable().template(\"\")\n                .build();\n        Assertions.assertFalse(strategyConfig.controller().isGenerate());\n        Assertions.assertFalse(strategyConfig.entity().isGenerate());\n        Assertions.assertFalse(strategyConfig.service().isGenerateService());\n        Assertions.assertFalse(strategyConfig.service().isGenerateServiceImpl());\n        Assertions.assertFalse(strategyConfig.mapper().isGenerateMapper());\n        Assertions.assertFalse(strategyConfig.mapper().isGenerateMapperXml());\n    }\n\n    @Test\n    void baseEntity() {\n        StrategyConfig strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(BaseEntity.class);\n        Set<String> columns = strategyConfig.entity().getSuperEntityColumns();\n        columns.forEach(System.out::println);\n        assertThat(columns).containsAll(Arrays.asList(\"deleted\", \"createTime\", \"id\"));\n        Assertions.assertEquals(3, columns.size());\n    }\n\n    @Test\n    void baseEntityNaming() {\n        StrategyConfig strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(BaseEntity.class).columnNaming(NamingStrategy.underline_to_camel);\n        Set<String> columns = strategyConfig.entity().getSuperEntityColumns();\n        columns.forEach(System.out::println);\n        assertThat(columns).containsAll(Arrays.asList(\"deleted\", \"create_time\", \"id\"));\n        Assertions.assertEquals(3, columns.size());\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().addSuperEntityColumns(\"aa\", \"bb\").entityBuilder().superClass(BaseEntity.class).columnNaming(NamingStrategy.underline_to_camel);\n        Assertions.assertEquals(5, strategyConfig.entity().getSuperEntityColumns().size());\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"aa\", \"bb\", \"deleted\", \"create_time\", \"id\"));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(BaseEntity.class).columnNaming(NamingStrategy.underline_to_camel).addSuperEntityColumns(\"aa\", \"bb\");\n        Assertions.assertEquals(5, strategyConfig.entity().getSuperEntityColumns().size());\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"aa\", \"bb\", \"deleted\", \"create_time\", \"id\"));\n    }\n\n    @Test\n    void superEntity() {\n        StrategyConfig strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(SuperEntity.class);\n        Set<String> columns = strategyConfig.entity().getSuperEntityColumns();\n        columns.forEach(System.out::println);\n        assertThat(columns).containsAll(Arrays.asList(\"deleted\", \"id\"));\n        Assertions.assertEquals(2, columns.size());\n    }\n\n    @Test\n    void testSuperAnnotation() {\n        StrategyConfig strategyConfig;\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(SuperBean.class).columnNaming(NamingStrategy.no_change);\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"test_id\", \"aa_name\", \"ok\", \"testName\"));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(SuperBean.class).columnNaming(NamingStrategy.no_change);\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"test_id\", \"aa_name\", \"ok\", \"testName\"));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(SuperBean.class).columnNaming(NamingStrategy.underline_to_camel);\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"test_id\", \"aa_name\", \"ok\", \"test_name\"));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().superClass(SuperBean.class).columnNaming(NamingStrategy.underline_to_camel);\n        assertThat(strategyConfig.entity().getSuperEntityColumns()).containsAll(Arrays.asList(\"test_id\", \"aa_name\", \"ok\", \"test_name\"));\n\n    }\n\n    @Test\n    void startsWithTablePrefixTest() {\n        StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        Assertions.assertFalse(strategyConfigBuilder.build().startsWithTablePrefix(\"t_name\"));\n        strategyConfigBuilder.addTablePrefix(\"a_\", \"t_\");\n        Assertions.assertTrue(strategyConfigBuilder.build().startsWithTablePrefix(\"t_name\"));\n    }\n\n    @Test\n    void addTableFillsTest() {\n        Column column = new Column(\"test\", FieldFill.INSERT);\n        List<IFill> columnList = new ArrayList<>();\n        columnList.add(column);\n        StrategyConfig strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().addTableFills(columnList);\n        Assertions.assertFalse(strategyConfig.entity().getTableFillList().isEmpty());\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().addTableFills(columnList);\n        Assertions.assertFalse(strategyConfig.entity().getTableFillList().isEmpty());\n    }\n\n    @Test\n    void entityNameConvertTest() {\n        TableInfo tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(),\n            TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfig(),\n            null, null, null), \"t_user\");\n\n        StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        Assertions.assertEquals(\"TUser\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n        strategyConfigBuilder.addTablePrefix(\"t_\", \"a_\");\n        Assertions.assertEquals(\"User\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        Assertions.assertEquals(\"TUser\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        strategyConfigBuilder.addTablePrefix(\"t_\", \"a_\");\n        Assertions.assertEquals(\"User\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        strategyConfigBuilder.addTableSuffix(\"_user\");\n        Assertions.assertEquals(\"T\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().nameConvert(new INameConvert() {\n            @Override\n            public @NotNull String entityNameConvert(@NotNull TableInfo tableInfo) {\n                return \"aaaa\";\n            }\n\n            @Override\n            public @NotNull String propertyNameConvert(@NotNull TableField field) {\n                return \"bbbb\";\n            }\n        });\n        Assertions.assertEquals(\"aaaa\", strategyConfigBuilder.build().entity().getNameConvert().entityNameConvert(tableInfo));\n    }\n\n    @Test\n    void propertyNameConvertTest() {\n        ConfigBuilder configBuilder;\n        StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfigBuilder.build(), null, null, null);\n        TableField tableField = new TableField(configBuilder,\"c_user_name\");\n        Assertions.assertEquals(\"cUserName\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n        strategyConfigBuilder.addFieldPrefix(\"t_\", \"c_\");\n        Assertions.assertEquals(\"userName\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        Assertions.assertEquals(\"cUserName\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        strategyConfigBuilder.addFieldPrefix(\"t_\", \"c_\");\n        Assertions.assertEquals(\"userName\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().naming(NamingStrategy.underline_to_camel);\n        strategyConfigBuilder.addFieldSuffix(\"_name\");\n        Assertions.assertEquals(\"cUser\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n\n        strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.entityBuilder().nameConvert(new INameConvert() {\n            @Override\n            public @NotNull String entityNameConvert(@NotNull TableInfo tableInfo) {\n                return \"aaaa\";\n            }\n\n            @Override\n            public @NotNull String propertyNameConvert(@NotNull TableField field) {\n                return \"bbbb\";\n            }\n        });\n        Assertions.assertEquals(\"bbbb\", strategyConfigBuilder.build().entity().getNameConvert().propertyNameConvert(tableField));\n    }\n\n    @Test\n    void matchExcludeTableTest() {\n        StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.addExclude(\"system\", \"user_1\", \"test[a|b]\");\n        StrategyConfig strategyConfig = strategyConfigBuilder.build();\n        Assertions.assertTrue(strategyConfig.matchExcludeTable(\"system\"));\n        Assertions.assertFalse(strategyConfig.matchExcludeTable(\"test_exclude\"));\n        Assertions.assertTrue(strategyConfig.matchExcludeTable(\"testa\"));\n        Assertions.assertTrue(strategyConfig.matchExcludeTable(\"testb\"));\n        Assertions.assertFalse(strategyConfig.matchExcludeTable(\"testc\"));\n    }\n\n    @Test\n    void matchIncludeTableTest() {\n        StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder();\n        strategyConfigBuilder.addInclude(\"system\", \"user_1\", \"test[a|b]\");\n        StrategyConfig strategyConfig = strategyConfigBuilder.build();\n        Assertions.assertTrue(strategyConfig.matchIncludeTable(\"system\"));\n        Assertions.assertFalse(strategyConfig.matchIncludeTable(\"test_exclude\"));\n        Assertions.assertTrue(strategyConfig.matchIncludeTable(\"testa\"));\n        Assertions.assertTrue(strategyConfig.matchIncludeTable(\"testb\"));\n        Assertions.assertFalse(strategyConfig.matchIncludeTable(\"testc\"));\n    }\n\n    @Test\n    void isCapitalModeNamingTest() {\n        Assertions.assertFalse(GeneratorBuilder.strategyConfig().isCapitalModeNaming(\"T_USER\"));\n        Assertions.assertFalse(GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build().isCapitalModeNaming(\"user\"));\n        Assertions.assertFalse(GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build().isCapitalModeNaming(\"user_name\"));\n        Assertions.assertTrue(GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build().isCapitalModeNaming(\"USER_NAME\"));\n        Assertions.assertTrue(GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build().isCapitalModeNaming(\"T_USER\"));\n        Assertions.assertTrue(GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build().isCapitalModeNaming(\"NAME\"));\n    }\n\n    private void buildAssert(StrategyConfig strategyConfig) {\n        Assertions.assertTrue(strategyConfig.isSkipView());\n        Assertions.assertTrue(strategyConfig.entity().isChain());\n        Assertions.assertTrue(strategyConfig.entity().isLombok());\n        Assertions.assertTrue(strategyConfig.entity().isSerialVersionUID());\n        Assertions.assertTrue(strategyConfig.entity().isFileOverride());\n        Assertions.assertTrue(strategyConfig.controller().isHyphenStyle());\n        Assertions.assertTrue(strategyConfig.controller().isRestStyle());\n        Assertions.assertFalse(strategyConfig.controller().isFileOverride());\n        Assertions.assertEquals(\"com.baomidou.mp.SuperController\", strategyConfig.controllerBuilder().get().getSuperClass());\n        Assertions.assertEquals(\"com.baomidou.mp.SuperMapper\", strategyConfig.mapper().getSuperClass());\n    }\n\n    @Test\n    void builderTest() {\n        StrategyConfig strategyConfig;\n        strategyConfig = GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().enableSkipView()\n            .entityBuilder().enableChainModel().enableLombok().enableFileOverride()\n            .controllerBuilder().enableHyphenStyle().enableRestStyle().superClass(\"com.baomidou.mp.SuperController\")\n            .mapperBuilder().superClass(\"com.baomidou.mp.SuperMapper\").build();\n\n        buildAssert(strategyConfig);\n        strategyConfig = GeneratorBuilder.strategyConfigBuilder().enableSkipView()\n            .entityBuilder().enableChainModel().enableLombok().enableFileOverride()\n            .controllerBuilder().superClass(\"com.baomidou.mp.SuperController\").enableHyphenStyle().enableRestStyle()\n            .mapperBuilder().superClass(\"com.baomidou.mp.SuperMapper\").build();\n        buildAssert(strategyConfig);\n    }\n\n    @Data\n    static class SuperBean {\n        @com.baomidou.mybatisplus.annotation.TableId(value = \"test_id\")\n        private String id;\n\n        @com.baomidou.mybatisplus.annotation.TableField(value = \"aa_name\")\n        private String name;\n\n        private String ok;\n\n        private String testName;\n\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/TemplateConfigTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config;\n\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong 2020/10/10.\n */\npublic class TemplateConfigTest {\n\n    @Test\n    void disableTest() {\n        TemplateConfig templateConfig;\n        templateConfig = GeneratorBuilder.templateConfig().disable();\n        Assertions.assertNull(templateConfig.getController());\n        Assertions.assertNull(templateConfig.getService());\n        Assertions.assertNull(templateConfig.getServiceImpl());\n        Assertions.assertNull(templateConfig.getMapper());\n        Assertions.assertNull(templateConfig.getXml());\n        Assertions.assertNull(templateConfig.getEntity(true));\n        Assertions.assertNull(templateConfig.getEntity(false));\n\n\n        templateConfig = GeneratorBuilder.templateConfig().disable(TemplateType.SERVICE);\n        Assertions.assertNull(templateConfig.getService());\n        Assertions.assertNotNull(templateConfig.getEntity(true));\n        Assertions.assertNotNull(templateConfig.getEntity(false));\n\n        templateConfig = GeneratorBuilder.templateConfig().disable(TemplateType.SERVICE_IMPL);\n        Assertions.assertNull(templateConfig.getServiceImpl());\n        Assertions.assertNotNull(templateConfig.getEntity(true));\n        Assertions.assertNotNull(templateConfig.getEntity(false));\n\n        templateConfig = GeneratorBuilder.templateConfig().disable(TemplateType.ENTITY);\n        Assertions.assertNotNull(templateConfig.getServiceImpl());\n        Assertions.assertNotNull(templateConfig.getService());\n        Assertions.assertNull(templateConfig.getEntity(true));\n        Assertions.assertNull(templateConfig.getEntity(false));\n\n        templateConfig = GeneratorBuilder.templateConfig().disable(TemplateType.ENTITY);\n        Assertions.assertNotNull(templateConfig.getServiceImpl());\n        Assertions.assertNotNull(templateConfig.getService());\n        Assertions.assertNull(templateConfig.getEntity(true));\n        Assertions.assertNull(templateConfig.getEntity(false));\n    }\n\n    @Test\n    void entityTest() {\n        Assertions.assertEquals(\"/templates/entity.kt\", GeneratorBuilder.templateConfig().getEntity(true));\n        Assertions.assertEquals(\"/templates/entity.java\", GeneratorBuilder.templateConfig().getEntity(false));\n        Assertions.assertEquals(\"/tm/entity.kt\", GeneratorBuilder.templateConfigBuilder().entityKt(\"/tm/entity.kt\").build().getEntity(true));\n        Assertions.assertEquals(\"/tm/entity.java\", GeneratorBuilder.templateConfigBuilder().entity(\"/tm/entity.java\").build().getEntity(false));\n        Assertions.assertEquals(\"/tm/entity.kt\", new TemplateConfig.Builder().entityKt(\"/tm/entity.kt\").build().getEntity(true));\n        Assertions.assertEquals(\"/tm/entity.java\", new TemplateConfig.Builder().entity(\"/tm/entity.java\").build().getEntity(false));\n        Assertions.assertEquals(\"myEntity.java.vm\", new TemplateConfig.Builder().entity(\"myEntity.java.vm\").build().getEntity(false));\n        Assertions.assertEquals(\"myEntity.kt.vm\", new TemplateConfig.Builder().entityKt(\"myEntity.kt.vm\").build().getEntity(true));\n    }\n\n    @Test\n    void builderTest() {\n        TemplateConfig templateConfig;\n        templateConfig = GeneratorBuilder.templateConfig();\n        Assertions.assertNotNull(templateConfig.getEntity(true));\n        Assertions.assertNotNull(templateConfig.getEntity(false));\n        Assertions.assertNotNull(templateConfig.getService());\n        Assertions.assertNotNull(templateConfig.getServiceImpl());\n\n        templateConfig = new TemplateConfig.Builder().entity(\"/tmp/entity.java\").entityKt(\"/tmp/entity.kt\").service(\"/tmp/service.java\").serviceImpl(\"/tmp/serviceImpl.java\").build();\n        Assertions.assertNotNull(templateConfig.getEntity(true));\n        Assertions.assertNotNull(templateConfig.getEntity(false));\n        Assertions.assertNotNull(templateConfig.getService());\n        Assertions.assertNotNull(templateConfig.getServiceImpl());\n        Assertions.assertEquals(\"/tmp/entity.kt\", templateConfig.getEntity(true));\n        Assertions.assertEquals(\"/tmp/entity.java\", templateConfig.getEntity(false));\n        Assertions.assertEquals(\"/tmp/service.java\", templateConfig.getService());\n        Assertions.assertEquals(\"/tmp/serviceImpl.java\", templateConfig.getServiceImpl());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/builder/ConfigBuilderTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.builder;\n\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.OutputFile;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2020/10/6.\n */\npublic class ConfigBuilderTest {\n\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\",\n        \"sa\", \"\").build();\n\n    @Test\n    void matcherRegTableTest() {\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"user\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"USER\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"t_user\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"T_USER\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"t_user_1\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"t_user_12\"));\n        Assertions.assertFalse(ConfigBuilder.matcherRegTable(\"t-user-12\"));\n        Assertions.assertTrue(ConfigBuilder.matcherRegTable(\"t_user_[0-9]\"));\n        Assertions.assertTrue(ConfigBuilder.matcherRegTable(\"t_user_\\\\d\"));\n        Assertions.assertTrue(ConfigBuilder.matcherRegTable(\"t_user_\\\\d{3,4}\"));\n        Assertions.assertTrue(ConfigBuilder.matcherRegTable(\"^t_.*\"));\n    }\n\n    @Test\n    void pathInfoTest() {\n        ConfigBuilder configBuilder;\n        Map<OutputFile, String> pathInfo;\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), DATA_SOURCE_CONFIG, GeneratorBuilder.strategyConfig(),\n            null, null, null);\n        pathInfo = configBuilder.getPathInfo();\n        Assertions.assertFalse(pathInfo.isEmpty());\n        Assertions.assertEquals(7, pathInfo.size());\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.entity));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.controller));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.service));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.serviceImpl));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.xml));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.mapper));\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.parent));\n\n        configBuilder = new ConfigBuilder(\n            GeneratorBuilder.packageConfigBuilder().pathInfo(Collections.singletonMap(OutputFile.entity,\n                \"/tmp/code/entity\")).build(), DATA_SOURCE_CONFIG, GeneratorBuilder.strategyConfig(),\n            null, null, null);\n        pathInfo = configBuilder.getPathInfo();\n        Assertions.assertFalse(pathInfo.isEmpty());\n        Assertions.assertEquals(7, pathInfo.size());\n        Assertions.assertTrue(pathInfo.containsKey(OutputFile.entity));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/converts/GaussDBSqlTypeConvertTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BIG_DECIMAL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.BOOLEAN;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.DATE_SQL;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.FLOAT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.INTEGER;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_DATE;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LOCAL_TIME;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.LONG;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.OBJECT;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.STRING;\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.TIME;\n\n/**\n * @author nieqiurong\n */\npublic class GaussDBSqlTypeConvertTest {\n\n    @Test\n    void test() {\n        GlobalConfig globalConfig = GeneratorBuilder.globalConfig();\n        GaussDBSqlTypeConvert convert = GaussDBSqlTypeConvert.INSTANCE;\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"char\"));\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"xml\"));\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"text\"));\n\n        Assertions.assertEquals(OBJECT, convert.processTypeConvert(globalConfig, \"uuid\"));\n        Assertions.assertEquals(OBJECT, convert.processTypeConvert(globalConfig, \"json\"));\n\n        Assertions.assertEquals(LONG, convert.processTypeConvert(globalConfig, \"bigint\"));\n        Assertions.assertEquals(INTEGER, convert.processTypeConvert(globalConfig, \"int\"));\n        Assertions.assertEquals(BOOLEAN, convert.processTypeConvert(globalConfig, \"bit\"));\n        Assertions.assertEquals(BIG_DECIMAL, convert.processTypeConvert(globalConfig, \"decimal\"));\n        Assertions.assertEquals(BIG_DECIMAL, convert.processTypeConvert(globalConfig, \"numeric\"));\n        Assertions.assertEquals(FLOAT, convert.processTypeConvert(globalConfig, \"float\"));\n\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.SQL_PACK).build();\n        Assertions.assertEquals(DATE_SQL, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(TIME, convert.processTypeConvert(globalConfig, \"time\"));\n\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.TIME_PACK).build();\n        Assertions.assertEquals(LOCAL_DATE, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(LOCAL_TIME, convert.processTypeConvert(globalConfig, \"time\"));\n\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.ONLY_DATE).build();\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"time\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/converts/SqlServerTypeConvertTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.converts;\n\nimport static com.baomidou.mybatisplus.generator.config.rules.DbColumnType.*;\n\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport com.baomidou.mybatisplus.generator.config.GlobalConfig;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\n\n/**\n * @author lanjerry 2020/10/23.\n */\npublic class SqlServerTypeConvertTest {\n\n    @Test\n    void processTypeConvertTest() {\n        // 常用格式\n        GlobalConfig globalConfig = GeneratorBuilder.globalConfig();\n        SqlServerTypeConvert convert = SqlServerTypeConvert.INSTANCE;\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"char\"));\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"xml\"));\n        Assertions.assertEquals(STRING, convert.processTypeConvert(globalConfig, \"text\"));\n        Assertions.assertEquals(LONG, convert.processTypeConvert(globalConfig, \"bigint\"));\n        Assertions.assertEquals(INTEGER, convert.processTypeConvert(globalConfig, \"int\"));\n        Assertions.assertEquals(BOOLEAN, convert.processTypeConvert(globalConfig, \"bit\"));\n        Assertions.assertEquals(DOUBLE, convert.processTypeConvert(globalConfig, \"decimal\"));\n        Assertions.assertEquals(DOUBLE, convert.processTypeConvert(globalConfig, \"numeric\"));\n        Assertions.assertEquals(BIG_DECIMAL, convert.processTypeConvert(globalConfig, \"money\"));\n        Assertions.assertEquals(BYTE_ARRAY, convert.processTypeConvert(globalConfig, \"binary\"));\n        Assertions.assertEquals(BYTE_ARRAY, convert.processTypeConvert(globalConfig, \"image\"));\n        Assertions.assertEquals(FLOAT, convert.processTypeConvert(globalConfig, \"float\"));\n        Assertions.assertEquals(FLOAT, convert.processTypeConvert(globalConfig, \"real\"));\n\n        // 日期格式\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.SQL_PACK).build();\n        Assertions.assertEquals(DATE_SQL, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(TIME, convert.processTypeConvert(globalConfig, \"time\"));\n        Assertions.assertEquals(TIMESTAMP, convert.processTypeConvert(globalConfig, \"timestamp\"));\n        Assertions.assertEquals(TIMESTAMP, convert.processTypeConvert(globalConfig, \"datetime\"));\n\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.TIME_PACK).build();\n        Assertions.assertEquals(LOCAL_DATE, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(LOCAL_TIME, convert.processTypeConvert(globalConfig, \"time\"));\n        Assertions.assertEquals(LOCAL_DATE_TIME, convert.processTypeConvert(globalConfig, \"timestamp\"));\n        Assertions.assertEquals(LOCAL_DATE_TIME, convert.processTypeConvert(globalConfig, \"datetime\"));\n\n        globalConfig = GeneratorBuilder.globalConfigBuilder().dateType(DateType.ONLY_DATE).build();\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"date\"));\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"time\"));\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"timestamp\"));\n        Assertions.assertEquals(DATE, convert.processTypeConvert(globalConfig, \"datetime\"));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/converts/TypeConvertsTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.converts;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong\n */\npublic class TypeConvertsTest {\n\n    @Test\n    void test() {\n        Assertions.assertInstanceOf(DmTypeConvert.class, TypeConverts.getTypeConvert(DbType.GAUSS));\n        Assertions.assertInstanceOf(GaussDBSqlTypeConvert.class, TypeConverts.getTypeConvert(DbType.GAUSS_DB));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/converts/select/SelectorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.converts.select;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author hanchunlin\n * Created at 2020/6/11 19:43\n */\nclass SelectorTest {\n\n    @Test\n    void test() {\n        assertEquals(\"1\", asString(1));\n        assertEquals(\"123\", asString(4));\n    }\n\n    /**\n     * 将数字转换为字符串\n     *\n     * @param i 数字\n     * @return 返回对应的字符串\n     */\n    private String asString(int i) {\n        Selector<Integer, String> selector = Selector.param(i);\n        return selector.test(BranchBuilder.<Integer, String>of(ii -> ii == 1).then(p -> \"1\"))\n            .test(BranchBuilder.<Integer, String>of(ii -> ii == 2).then(p -> \"2\"))\n            .test(BranchBuilder.<Integer, String>of(ii -> ii == 3).then(p -> \"3\"))\n            .or(\"123\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/po/TableFieldTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.po;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport com.baomidou.mybatisplus.generator.fill.Column;\nimport com.baomidou.mybatisplus.generator.fill.Property;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong 2020/10/8.\n */\npublic class TableFieldTest {\n\n    @Test\n    void convertTest() {\n        ConfigBuilder configBuilder;\n        TableField tableField;\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfig(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertFalse(new TableField(configBuilder, \"desc\").setColumnName(\"desc\").setPropertyName(\"desc\", DbColumnType.STRING).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"desc\").setColumnName(\"desc\").setPropertyName(\"desc1\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"DESC\").setColumnName(\"DESC\").setPropertyName(\"desc\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"desc\").setColumnName(\"desc\").setPropertyName(\"desc\", DbColumnType.STRING).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"desc\").setColumnName(\"desc\").setPropertyName(\"desc1\", DbColumnType.STRING).isConvert());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().enableTableFieldAnnotation().build(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"name\").setColumnName(\"name\").setPropertyName(\"name\", DbColumnType.STRING).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"name\").setColumnName(\"name\").setPropertyName(\"name1\", DbColumnType.STRING).isConvert());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().enableChainModel().build(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertFalse(new TableField(configBuilder, \"NAME\").setColumnName(\"NAME\").setPropertyName(\"name\", DbColumnType.STRING).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"USER_NAME\").setColumnName(\"USER_NAME\").setPropertyName(\"userName1\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"USER_NAME\").setColumnName(\"USER_NAME\").setPropertyName(\"userName\", DbColumnType.STRING).isConvert());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().columnNaming(NamingStrategy.underline_to_camel).build(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertFalse(new TableField(configBuilder, \"user_name\").setColumnName(\"user_name\").setPropertyName(\"userName\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"USER_NAME\").setColumnName(\"USER_NAME\").setPropertyName(\"userName\", DbColumnType.STRING).isConvert());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().columnNaming(NamingStrategy.no_change).build(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"user_name\").setColumnName(\"user_name\").setPropertyName(\"userName\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"USER_NAME\").setColumnName(\"USER_NAME\").setPropertyName(\"USER_NAME\", DbColumnType.STRING).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"NAME\").setColumnName(\"NAME\").setPropertyName(\"name\", DbColumnType.STRING).isConvert());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().enableRemoveIsPrefix().build(), null, GeneratorBuilder.globalConfig(), null);\n        tableField = new TableField(configBuilder, \"delete\").setColumnName(\"delete\").setPropertyName(\"delete\", DbColumnType.BOOLEAN);\n        Assertions.assertEquals(\"delete\", tableField.getPropertyName());\n        Assertions.assertFalse(tableField.isConvert());\n        tableField = new TableField(configBuilder, \"delete\").setColumnName(\"delete\").setPropertyName(\"delete\", DbColumnType.BOOLEAN);\n        Assertions.assertEquals(\"delete\", tableField.getPropertyName());\n        Assertions.assertFalse(tableField.isConvert());\n        tableField = new TableField(configBuilder, \"is_delete\").setColumnName(\"is_delete\").setPropertyName(\"isDelete\", DbColumnType.BOOLEAN);\n        Assertions.assertEquals(\"delete\", tableField.getPropertyName());\n        Assertions.assertTrue(tableField.isConvert());\n        tableField = new TableField(configBuilder, \"is_delete\").setColumnName(\"is_delete\").setPropertyName(\"isDelete\", DbColumnType.BOOLEAN);\n        Assertions.assertEquals(\"delete\", tableField.getPropertyName());\n        Assertions.assertTrue(tableField.isConvert());\n    }\n\n    @Test\n    void versionFieldTest() {\n        ConfigBuilder configBuilder;\n        StrategyConfig strategyConfig;\n        strategyConfig = new StrategyConfig.Builder().entityBuilder().versionColumnName(\"c_version\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertFalse(new TableField(configBuilder, \"version\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n        Assertions.assertFalse(new TableField(configBuilder, \"version\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n        Assertions.assertTrue(new TableField(configBuilder, \"c_version\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n        Assertions.assertTrue(new TableField(configBuilder, \"C_VERSION\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n\n        strategyConfig = new StrategyConfig.Builder().entityBuilder().versionPropertyName(\"version\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"version\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n        Assertions.assertTrue(new TableField(configBuilder, \"VERSION\").setPropertyName(\"version\", DbColumnType.LONG).isVersionField());\n        Assertions.assertFalse(new TableField(configBuilder, \"c_version\").setPropertyName(\"cVersion\", DbColumnType.LONG).isVersionField());\n        Assertions.assertFalse(new TableField(configBuilder, \"C_VERSION\").setPropertyName(\"cVersion\", DbColumnType.LONG).isVersionField());\n    }\n\n    @Test\n    void logicDeleteFiledTest() {\n        ConfigBuilder configBuilder;\n        StrategyConfig strategyConfig;\n        strategyConfig = new StrategyConfig.Builder().entityBuilder().logicDeleteColumnName(\"delete\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"DELETE\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertTrue(new TableField(configBuilder, \"delete\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n\n        strategyConfig = new StrategyConfig.Builder().entityBuilder().logicDeletePropertyName(\"delete\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"IS_DELETE\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertTrue(new TableField(configBuilder, \"is_delete\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertFalse(new TableField(configBuilder, \"is_delete\").setPropertyName(\"isDelete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n\n        strategyConfig = new StrategyConfig.Builder().entityBuilder().enableRemoveIsPrefix().logicDeletePropertyName(\"delete\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"IS_DELETE\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertTrue(new TableField(configBuilder, \"is_delete\").setPropertyName(\"delete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertTrue(new TableField(configBuilder, \"is_delete\").setPropertyName(\"isDelete\", DbColumnType.BOOLEAN).isLogicDeleteField());\n        Assertions.assertFalse(new TableField(configBuilder, \"is_delete\").setPropertyName(\"isDelete\", DbColumnType.INTEGER).isLogicDeleteField());\n    }\n\n    @Test\n    void fillTest() {\n        ConfigBuilder configBuilder;\n        StrategyConfig strategyConfig;\n        strategyConfig = new StrategyConfig.Builder()\n            .entityBuilder()\n            .addTableFills(\n                new Column(\"create_time\", FieldFill.INSERT), new Column(\"update_time\", FieldFill.UPDATE),\n                new Property(\"createBy\", FieldFill.INSERT), new Property(\"updateBy\", FieldFill.INSERT),\n                new Column(\"create_user\")\n            ).build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertNotNull(new TableField(configBuilder, \"create_time\").getFill());\n        Assertions.assertNotNull(new TableField(configBuilder, \"update_time\").getFill());\n        Assertions.assertNull(new TableField(configBuilder, \"name\").getFill());\n        Assertions.assertNull(new TableField(configBuilder, \"createBy\").getFill());\n        Assertions.assertNull(new TableField(configBuilder, \"updateBy\").getFill());\n        Assertions.assertNull(new TableField(configBuilder, \"create_by\").getFill());\n        Assertions.assertNull(new TableField(configBuilder, \"update_by\").getFill());\n        Assertions.assertNotNull(new TableField(configBuilder, \"createBy\").setPropertyName(\"createBy\", DbColumnType.STRING).getFill());\n        Assertions.assertNotNull(new TableField(configBuilder, \"updateBy\").setPropertyName(\"createBy\", DbColumnType.STRING).getFill());\n        Assertions.assertNotNull(new TableField(configBuilder, \"create_user\").setPropertyName(\"createUser\", DbColumnType.STRING).getFill());\n    }\n\n    @Test\n    void testIdConvert(){\n        ConfigBuilder configBuilder;\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfig(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertFalse(new TableField(configBuilder, \"id\").primaryKey(true).setPropertyName(\"id\", DbColumnType.LONG).isConvert());\n        Assertions.assertFalse(new TableField(configBuilder, \"id\").primaryKey(false).setPropertyName(\"id\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(true).setPropertyName(\"ids\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(false).setPropertyName(\"ids\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"user_id\").primaryKey(true).setPropertyName(\"userId\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"user_id\").primaryKey(false).setPropertyName(\"userId\", DbColumnType.LONG).isConvert());\n        // 开启tableFieldAnnotationEnable\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig, GeneratorBuilder.strategyConfig().entityBuilder().enableTableFieldAnnotation().build(), null, GeneratorBuilder.globalConfig(), null);\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(true).setPropertyName(\"id\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(false).setPropertyName(\"id\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(true).setPropertyName(\"ids\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"id\").primaryKey(false).setPropertyName(\"ids\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"user_id\").primaryKey(true).setPropertyName(\"userId\", DbColumnType.LONG).isConvert());\n        Assertions.assertTrue(new TableField(configBuilder, \"user_id\").primaryKey(false).setPropertyName(\"userId\", DbColumnType.LONG).isConvert());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/po/TableInfoTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.po;\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.INameConvert;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;\nimport com.baomidou.mybatisplus.generator.fill.Column;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n\n/**\n * @author nieqiurong 2020/9/21.\n */\npublic class TableInfoTest {\n\n    public static final DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\").build();\n\n    @Test\n    void getFieldNamesTest() {\n        TableInfo tableInfo;\n        ConfigBuilder configBuilder;\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"name\");\n        tableInfo.addField(new TableField(configBuilder, \"name\").setColumnName(\"name\"));\n        Assertions.assertEquals(\"name\", tableInfo.getFieldNames());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"name\");\n        tableInfo.addField(new TableField(configBuilder, \"name\").setColumnName(\"name\"));\n        tableInfo.addField(new TableField(configBuilder, \"age\").setColumnName(\"age\"));\n        Assertions.assertEquals(\"name, age\", tableInfo.getFieldNames());\n\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"name\");\n        tableInfo.addField(new TableField(configBuilder, \"name\").setColumnName(\"name\"));\n        tableInfo.addField(new TableField(configBuilder, \"age\").setColumnName(\"age\"));\n        tableInfo.addField(new TableField(configBuilder, \"phone\").setColumnName(\"phone\"));\n        Assertions.assertEquals(\"name, age, phone\", tableInfo.getFieldNames());\n    }\n\n    @Test\n    void processTableTest() {\n        StrategyConfig strategyConfig = GeneratorBuilder.strategyConfig();\n        TableInfo tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null), \"user\");\n        tableInfo.processTable();\n        Assertions.assertFalse(tableInfo.isConvert());\n        Assertions.assertEquals(\"UserMapper\", tableInfo.getMapperName());\n        Assertions.assertEquals(\"UserMapper\", tableInfo.getXmlName());\n        Assertions.assertEquals(\"IUserService\", tableInfo.getServiceName());\n        Assertions.assertEquals(\"UserServiceImpl\", tableInfo.getServiceImplName());\n        Assertions.assertEquals(\"UserController\", tableInfo.getControllerName());\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().formatFileName(\"%sEntity\")\n            .mapperBuilder().formatMapperFileName(\"%sDao\").formatXmlFileName(\"%sXml\")\n            .controllerBuilder().formatFileName(\"%sAction\")\n            .serviceBuilder().formatServiceFileName(\"%sService\").formatServiceImplFileName(\"%sServiceImp\");\n        tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig,\n            null, null, null), \"user\");\n        tableInfo.processTable();\n        Assertions.assertTrue(tableInfo.isConvert());\n        Assertions.assertEquals(\"UserEntity\", tableInfo.getEntityName());\n        Assertions.assertEquals(\"UserDao\", tableInfo.getMapperName());\n        Assertions.assertEquals(\"UserXml\", tableInfo.getXmlName());\n        Assertions.assertEquals(\"UserService\", tableInfo.getServiceName());\n        Assertions.assertEquals(\"UserServiceImp\", tableInfo.getServiceImplName());\n        Assertions.assertEquals(\"UserAction\", tableInfo.getControllerName());\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        strategyConfig.entityBuilder().nameConvert(new INameConvert() {\n            @Override\n            public @NotNull String entityNameConvert(@NotNull TableInfo tableInfo) {\n                return \"E\" + tableInfo.getName();\n            }\n\n            @Override\n            public @NotNull String propertyNameConvert(@NotNull TableField field) {\n                return field.getName();\n            }\n        });\n        tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, null, null), \"user\");\n        tableInfo.processTable();\n        Assertions.assertTrue(tableInfo.isConvert());\n        Assertions.assertEquals(\"Euser\", tableInfo.getEntityName());\n    }\n\n    @Test\n    void importPackageTest() {\n        TableInfo tableInfo;\n        StrategyConfig strategyConfig;\n        ConfigBuilder configBuilder;\n        tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null), \"user\");\n        tableInfo.importPackage();\n        Assertions.assertEquals(1, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n\n        tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null), \"user\").setEntityName(\"userEntity\").setConvert();\n        tableInfo.importPackage();\n        Assertions.assertEquals(2, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableName.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfigBuilder().entityBuilder().superClass(\"con.baomihua.demo.SuperEntity\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"user\");\n        tableInfo.importPackage();\n        Assertions.assertEquals(2, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(\"con.baomihua.demo.SuperEntity\"));\n\n        tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder()\n            .entityBuilder().enableActiveRecord().build(), null, null, null), \"user\");\n        tableInfo.importPackage();\n        Assertions.assertEquals(2, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Model.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        tableInfo = new TableInfo(configBuilder, \"user\");\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").setColumnName(\"u_id\").primaryKey(true).setPropertyName(\"uid\", DbColumnType.LONG));\n        tableInfo.importPackage();\n        Assertions.assertEquals(3, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        tableInfo = new TableInfo(configBuilder, \"user\");\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").setPropertyName(\"uid\", DbColumnType.LONG).primaryKey(true));\n        tableInfo.importPackage();\n        Assertions.assertEquals(3, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfigBuilder().entityBuilder().logicDeleteColumnName(\"delete_flag\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        tableInfo = new TableInfo(configBuilder, \"user\");\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").setColumnName(\"u_id\").primaryKey(true).setPropertyName(\"uid\", DbColumnType.LONG));\n        tableInfo.addField(new TableField(configBuilder, \"delete_flag\").setColumnName(\"delete_flag\").setPropertyName(\"deleteFlag\", DbColumnType.BOOLEAN));\n        tableInfo.importPackage();\n        Assertions.assertEquals(4, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableLogic.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig.entityBuilder().idType(IdType.ASSIGN_ID).build(), null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"user\");\n        tableInfo.addField(new TableField(configBuilder, \"name\").setPropertyName(\"name\", DbColumnType.STRING));\n        tableInfo.importPackage();\n        Assertions.assertEquals(1, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfig();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig.entityBuilder().idType(IdType.ASSIGN_ID).build(), null, null, null);\n        tableInfo = new TableInfo(configBuilder, \"user\").setHavePrimaryKey(true);\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").setColumnName(\"u_id\").primaryKey(true).setPropertyName(\"uid\", DbColumnType.LONG));\n        tableInfo.importPackage();\n        Assertions.assertEquals(3, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfig().entityBuilder().addTableFills(new Column(\"create_time\", FieldFill.DEFAULT)).build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        tableInfo = new TableInfo(configBuilder, \"user\").setHavePrimaryKey(true);\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").setColumnName(\"u_id\").primaryKey(true).setPropertyName(\"uid\", DbColumnType.LONG));\n        tableInfo.addField(new TableField(configBuilder, \"create_time\").setColumnName(\"create_time\").setPropertyName(\"createTime\", DbColumnType.DATE));\n        tableInfo.importPackage();\n        Assertions.assertEquals(6, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Date.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(com.baomidou.mybatisplus.annotation.TableField.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(FieldFill.class.getName()));\n\n        strategyConfig = GeneratorBuilder.strategyConfigBuilder().entityBuilder().versionColumnName(\"version\").build();\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, strategyConfig, null, GeneratorBuilder.globalConfig(), null);\n        tableInfo = new TableInfo(configBuilder, \"user\").setHavePrimaryKey(true);\n        tableInfo.addField(new TableField(configBuilder, \"u_id\").primaryKey(true).setPropertyName(\"uid\", DbColumnType.LONG));\n        tableInfo.addField(new TableField(configBuilder, \"version\").setPropertyName(\"version\", DbColumnType.LONG));\n        tableInfo.importPackage();\n        Assertions.assertEquals(4, tableInfo.getImportPackages().size());\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Serializable.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(IdType.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(TableId.class.getName()));\n        Assertions.assertTrue(tableInfo.getImportPackages().contains(Version.class.getName()));\n    }\n\n    @Test\n    void setEntityNameTest() {\n        ConfigBuilder configBuilder;\n        Assertions.assertTrue(new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null), \"user\").setEntityName(\"UserEntity\").isConvert());\n        Assertions.assertFalse(new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfig(), null, null, null), \"user\").setEntityName(\"User\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build(), null, null, null);\n        Assertions.assertFalse(new TableInfo(configBuilder, \"USER\").setEntityName(\"User\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().enableCapitalMode().build(), null, null, null);\n        Assertions.assertTrue(new TableInfo(configBuilder, \"USER\").setEntityName(\"UserEntity\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().naming(NamingStrategy.no_change).build(), null, null, null);\n        Assertions.assertTrue(new TableInfo(configBuilder, \"test_user\").setEntityName(\"TestUser\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().naming(NamingStrategy.no_change).build(), null, null, null);\n        Assertions.assertFalse(new TableInfo(configBuilder, \"user\").setEntityName(\"User\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().naming(NamingStrategy.underline_to_camel).build(), null, null, null);\n        Assertions.assertTrue(new TableInfo(configBuilder, \"test_user\").setEntityName(\"TestUser\").isConvert());\n        configBuilder = new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig, GeneratorBuilder.strategyConfigBuilder().entityBuilder().naming(NamingStrategy.underline_to_camel).build(), null, null, null);\n        Assertions.assertTrue(new TableInfo(configBuilder, \"TEST_USER\").setEntityName(\"TestUser\").isConvert());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/config/rules/NamingStrategyTest.java",
    "content": "package com.baomidou.mybatisplus.generator.config.rules;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashSet;\n\npublic class NamingStrategyTest {\n\n    @Test\n    void removePrefixTest() {\n        Assertions.assertEquals(\"_ab\", NamingStrategy.removePrefix(\"test_ab\", new HashSet<>() {{\n            add(\"t_\");\n            add(\"test\");\n        }}));\n    }\n\n    @Test\n    void underlineToCamelTest() {\n        Assertions.assertEquals(\"aid\", NamingStrategy.underlineToCamel(\"Aid\"));\n        Assertions.assertEquals(\"aId\", NamingStrategy.underlineToCamel(\"AId\"));\n        Assertions.assertEquals(\"testId\", NamingStrategy.underlineToCamel(\"test_id\"));\n        Assertions.assertEquals(\"testId\", NamingStrategy.underlineToCamel(\"TEST_ID\"));\n        Assertions.assertEquals(\"testId\", NamingStrategy.underlineToCamel(\"Test_id\"));\n        Assertions.assertEquals(\"testId\", NamingStrategy.underlineToCamel(\"Test_ID\"));\n        Assertions.assertEquals(\"testId\", NamingStrategy.underlineToCamel(\"TeSt_id\"));\n        Assertions.assertEquals(\"createTime\", NamingStrategy.underlineToCamel(\"createTime\"));\n        Assertions.assertEquals(\"createTime\", NamingStrategy.underlineToCamel(\"create_time\"));\n        Assertions.assertEquals(\"createTime\", NamingStrategy.underlineToCamel(\"create_Time\"));\n        Assertions.assertEquals(\"createTime\", NamingStrategy.underlineToCamel(\"Create_Time\"));\n        Assertions.assertEquals(\"createtime\", NamingStrategy.underlineToCamel(\"CREATETIME\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/engine/TemplateEngineTest.java",
    "content": "package com.baomidou.mybatisplus.generator.engine;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;\nimport com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder;\nimport com.baomidou.mybatisplus.generator.config.po.LikeTable;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfoTest;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Map;\n\n/**\n * @author nieqiurong 2020/10/11.\n */\npublic class TemplateEngineTest {\n\n    private void compatibleAssert(ConfigBuilder configBuilder) {\n        VelocityTemplateEngine velocityTemplateEngine = new VelocityTemplateEngine();\n        velocityTemplateEngine.setConfigBuilder(configBuilder);\n        TableInfo tableInfo = new TableInfo(new ConfigBuilder(GeneratorBuilder.packageConfig(), TableInfoTest.dataSourceConfig,\n            GeneratorBuilder.strategyConfig(), null, null, null), \"user\");\n        tableInfo.processTable();\n        Map<String, Object> objectMap = velocityTemplateEngine.getObjectMap(configBuilder, tableInfo);\n        Assertions.assertEquals(Boolean.FALSE, objectMap.get(\"enableCache\"));\n        Assertions.assertEquals(Boolean.TRUE, objectMap.get(\"baseResultMap\"));\n        Assertions.assertEquals(Boolean.TRUE, objectMap.get(\"baseColumnList\"));\n        Assertions.assertEquals(Boolean.TRUE, objectMap.get(\"activeRecord\"));\n        Assertions.assertEquals(IdType.INPUT.toString(), objectMap.get(\"idType\"));\n    }\n\n    @Test\n    void compatibleTest() {\n        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\").build();\n        compatibleAssert(new ConfigBuilder(GeneratorBuilder.packageConfig(), dataSourceConfig,\n            new StrategyConfig.Builder().likeTable(new LikeTable(\"USER\"))\n                .entityBuilder().enableActiveRecord().idType(IdType.INPUT)\n                .mapperBuilder().enableBaseResultMap().enableBaseColumnList()\n                .build(),\n            null, GeneratorBuilder.globalConfig(), null));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/entity/BaseEntity.java",
    "content": "package com.baomidou.mybatisplus.generator.entity;\n\nimport java.util.Date;\n\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n * <p>\n * 测试的基础父类\n * </p>\n *\n * @author hubin\n * @since 2019-02-20\n */\n@Data\n@EqualsAndHashCode(callSuper = true)\npublic class BaseEntity extends SuperEntity {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = 199327361052220940L;\n\tprivate Boolean deleted;\n    private Date createTime;\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/entity/SuperEntity.java",
    "content": "package com.baomidou.mybatisplus.generator.entity;\n\nimport java.io.Serializable;\n\nimport lombok.Data;\n\n/**\n * <p>\n * 测试的基础父类\n * </p>\n *\n * @author hubin\n * @since 2019-02-20\n */\n@Data\npublic class SuperEntity implements Serializable {\n\n    /**\n\t * serialVersionUID\n\t */\n\tprivate static final long serialVersionUID = -4801865210961587582L;\n\n\tprivate Long id;\n    private Boolean deleted;\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/index/MapperMethodHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.generator.index;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\npublic class MapperMethodHandlerTest {\n\n    @Test\n    void testIsPrimaryKey() {\n        var method = new DefaultGenerateMapperMethodHandler();\n        Assertions.assertTrue(method.isPrimaryKey(\"PRIMARY\"));\n        Assertions.assertTrue(method.isPrimaryKey(\"primary\"));\n        Assertions.assertTrue(method.isPrimaryKey(\"PRIMARY_KEY_1\"));\n        Assertions.assertTrue(method.isPrimaryKey(\"primary_key_1\"));\n        Assertions.assertFalse(method.isPrimaryKey(\"id_idx\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/jdbc/DatabaseMetaDataWrapperTest.java",
    "content": "package com.baomidou.mybatisplus.generator.jdbc;\n\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Map;\n\npublic class DatabaseMetaDataWrapperTest {\n\n    @Test\n    void test() {\n        DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\", \"sa\", \"\").build();\n        DatabaseMetaDataWrapper databaseMetaDataWrapper = new DatabaseMetaDataWrapper(dataSourceConfig.getConn(), dataSourceConfig.getSchemaName());\n        Map<String, DatabaseMetaDataWrapper.Column> columnsInfo = databaseMetaDataWrapper.getColumnsInfo(null, null, \"USERS\", true);\n        Assertions.assertNotNull(columnsInfo);\n        DatabaseMetaDataWrapper.Column name = columnsInfo.get(\"user_name\");\n        Assertions.assertTrue(name.isNullable());\n        Assertions.assertEquals(JdbcType.VARCHAR, name.getJdbcType());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/keywords/H2KeyWordsHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.generator.keywords;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/**\n * @author nieqiurong 2020/5/8.\n */\nclass H2KeyWordsHandlerTest {\n\n    @Test\n    void test() {\n        H2KeyWordsHandler keyWordsHandler = new H2KeyWordsHandler();\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"CHECK\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"check\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"USER_NAME\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"user_name\"));\n        Assertions.assertEquals(\"\\\"CHECK\\\"\", keyWordsHandler.formatColumn(\"CHECK\"));\n\n        keyWordsHandler.getKeyWords().remove(\"CHECK\");\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"CHECK\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"check\"));\n\n        keyWordsHandler = new H2KeyWordsHandler(new ArrayList<>(Arrays.asList(\"TEST\",\"AAA\")));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"TEST\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"test\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"AAA\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"aaa\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"FILE\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"file\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/keywords/MySqlKeyWordsHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.generator.keywords;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/**\n * @author nieqiurong 2020/5/8.\n */\nclass MySqlKeyWordsHandlerTest {\n\n    @Test\n    void test() {\n        MySqlKeyWordsHandler keyWordsHandler = new MySqlKeyWordsHandler();\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"FILE\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"file\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"USER_NAME\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"user_name\"));\n        Assertions.assertEquals(\"`FILE`\", keyWordsHandler.formatColumn(\"FILE\"));\n\n        keyWordsHandler.getKeyWords().remove(\"FILE\");\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"FILE\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"file\"));\n\n        keyWordsHandler = new MySqlKeyWordsHandler(new ArrayList<>(Arrays.asList(\"TEST\",\"AAA\")));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"TEST\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"test\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"AAA\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"aaa\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"FILE\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"file\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/keywords/PostgreSqlKeyWordsHandlerTest.java",
    "content": "package com.baomidou.mybatisplus.generator.keywords;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong 2020/5/9.\n */\nclass PostgreSqlKeyWordsHandlerTest {\n\n    @Test\n    void test() {\n        PostgreSqlKeyWordsHandler keyWordsHandler = new PostgreSqlKeyWordsHandler();\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"with\"));\n        Assertions.assertTrue(keyWordsHandler.isKeyWords(\"WITH\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"system\"));\n        Assertions.assertFalse(keyWordsHandler.isKeyWords(\"SYSTEM\"));\n        Assertions.assertEquals(\"\\\"with\\\"\", keyWordsHandler.formatColumn(\"with\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/model/ClassAnnotationAttributesTest.java",
    "content": "package com.baomidou.mybatisplus.generator.model;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.baomidou.mybatisplus.generator.config.po.TableInfo;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\n\nimport java.util.Set;\n\npublic class ClassAnnotationAttributesTest {\n\n    @Test\n    void test() {\n        var classAnnotationAttributes = new ClassAnnotationAttributes(TableName.class);\n        Assertions.assertNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@TableName\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(TableName.class.getName()));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(TableName.class,(tableInfo -> \"@Test\"));\n        classAnnotationAttributes.handleDisplayName(Mockito.mock(TableInfo.class));\n        Assertions.assertNotNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(TableName.class.getName()));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(TableName.class,(tableInfo -> \"@Test\"), \"com.baomidou.test\");\n        classAnnotationAttributes.handleDisplayName(Mockito.mock(TableInfo.class));\n        Assertions.assertNotNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(TableName.class.getName()));\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test\"));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(TableName.class, \"@Test\");\n        Assertions.assertNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(TableName.class.getName()));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(TableName.class, \"@Test\", \"com.baomidou.test\");\n        Assertions.assertNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(TableName.class.getName()));\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test\"));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(\"@Test\", \"com.baomidou.test\");\n        Assertions.assertNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test\"));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(\"com.baomidou.test\", (tableInfo -> \"@Test\"));\n        classAnnotationAttributes.handleDisplayName(Mockito.mock(TableInfo.class));\n        Assertions.assertNotNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test\"));\n\n        classAnnotationAttributes = new ClassAnnotationAttributes(Set.of(\"com.baomidou.test1\",\"com.baomidou.test2\"), (tableInfo -> \"@Test\"));\n        classAnnotationAttributes.handleDisplayName(Mockito.mock(TableInfo.class));\n        Assertions.assertNotNull(classAnnotationAttributes.getDisplayNameFunction());\n        Assertions.assertEquals(\"@Test\", classAnnotationAttributes.getDisplayName());\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test1\"));\n        Assertions.assertTrue(classAnnotationAttributes.getImportPackages().contains(\"com.baomidou.test2\"));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/BaseGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.config.*;\nimport org.apache.ibatis.jdbc.ScriptRunner;\n\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * 基础测试类\n *\n * @author lanjerry\n * @since 3.5.3\n */\npublic class BaseGeneratorTest {\n\n    /**\n     * 执行数据库脚本\n     */\n    protected static void initDataSource(DataSourceConfig dataSourceConfig) throws SQLException {\n        Connection conn = dataSourceConfig.getConn();\n        InputStream inputStream = H2CodeGeneratorTest.class.getResourceAsStream(\"/sql/init.sql\");\n        ScriptRunner scriptRunner = new ScriptRunner(conn);\n        scriptRunner.setAutoCommit(true);\n        scriptRunner.runScript(new InputStreamReader(inputStream));\n        conn.close();\n    }\n\n    /**\n     * 策略配置\n     */\n    protected static StrategyConfig.Builder strategyConfig() {\n        return new StrategyConfig.Builder();\n    }\n\n    /**\n     * 全局配置\n     */\n    protected static GlobalConfig.Builder globalConfig() {\n        return new GlobalConfig.Builder();\n    }\n\n    /**\n     * 包配置\n     */\n    protected static PackageConfig.Builder packageConfig() {\n        return new PackageConfig.Builder();\n    }\n\n    /**\n     * 注入配置\n     */\n    protected static InjectionConfig.Builder injectionConfig() {\n        // 测试自定义输出文件之前注入操作，该操作再执行生成代码前 debug 查看\n        return new InjectionConfig.Builder().beforeOutputFile((tableInfo, objectMap) -> {\n            System.out.println(\"tableInfo: \" + tableInfo.getEntityName() + \" objectMap: \" + objectMap.size());\n        });\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/DMGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n/**\n * 达梦 代码生成\n *\n * @author lanjerry\n * @since 3.5.3\n */\npublic class DMGeneratorTest extends BaseGeneratorTest {\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig\n        .Builder(\"jdbc:dm://xxxx:5236/DMSERVER?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8\", \"SYSDBA\", \"SYSDBA\")\n        .schema(\"SYSDBA\")\n        .build();\n\n    public static void main(String[] args) {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/FastAutoGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.FastAutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.engine.EnjoyTemplateEngine;\n\nimport java.sql.SQLException;\n\n/**\n * <p>\n * 快速生成\n * </p>\n *\n * @author lanjerry\n * @since 2021-09-16\n */\npublic class FastAutoGeneratorTest extends BaseGeneratorTest {\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig\n        .Builder(\"jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;MODE=MYSQL\", \"sa\", \"\");\n\n    /**\n     * 执行 run\n     */\n    public static void main(String[] args) throws SQLException {\n        // 初始化数据库脚本\n        initDataSource(DATA_SOURCE_CONFIG.build());\n        FastAutoGenerator.create(DATA_SOURCE_CONFIG)\n            // 数据库配置\n            .dataSourceConfig((scanner, builder) -> builder.schema(scanner.apply(\"请输入表名称\")))\n            // 全局配置\n            .globalConfig((scanner, builder) -> builder.author(scanner.apply(\"请输入作者名称\")))\n            // 包配置\n            .packageConfig((scanner, builder) -> builder.parent(scanner.apply(\"请输入包名\")))\n            // 策略配置\n            .strategyConfig((scanner, builder) -> builder.addInclude(scanner.apply(\"请输入表名，多个表名用,隔开\")))\n            /*\n                模板引擎配置，默认 Velocity 可选模板引擎 Beetl 或 Freemarker 或 Enjoy\n               .templateEngine(new BeetlTemplateEngine())\n               .templateEngine(new FreemarkerTemplateEngine())\n               .templateEngine(new EnjoyTemplateEngine())\n             */\n            .execute();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/GaussDBGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\nimport com.baomidou.mybatisplus.generator.config.StrategyConfig;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.query.DefaultQuery;\nimport com.baomidou.mybatisplus.generator.query.SQLQuery;\nimport org.apache.ibatis.jdbc.SqlRunner;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.Types;\n\n/**\n * @author nieqiurong\n * @since 3.5.13\n */\npublic class GaussDBGeneratorTest {\n\n    private static final String CRATE_TABLE_DDL = \"\"\"\n\n        DROP TABLE IF EXISTS \"t_user\";\n\n        CREATE TABLE \"t_user\" (\n          \"id\" bigserial,\n          \"name\" varchar(50),\n          \"age\" int4,\n          \"sex\" int2,\n          \"enable\" bool DEFAULT true,\n          \"ext_json\" json,\n          \"birthday\" date,\n          \"status\" char,\n          \"amount\" decimal(10,2),\n          \"score\" numeric,\n          \"creat_time\" timestamp,\n          \"describe\" text,\n          \"uid\" uuid,\n          PRIMARY KEY (\"id\")\n        );\n\n        COMMENT ON COLUMN \"t_user\".\"name\" IS '姓名';\n\n        COMMENT ON COLUMN \"t_user\".\"age\" IS '年龄';\n\n        COMMENT ON COLUMN \"t_user\".\"sex\" IS '性别(0:未知 1:男 2:女)';\n\n        COMMENT ON COLUMN \"t_user\".\"enable\" IS '是否启用';\n\n        COMMENT ON COLUMN \"t_user\".\"ext_json\" IS '扩展信息JSON';\n\n        COMMENT ON COLUMN \"t_user\".\"birthday\" IS '生日';\n\n        COMMENT ON COLUMN \"t_user\".\"status\" IS '状态（A:正常, D:禁用）';\n\n        COMMENT ON COLUMN \"t_user\".\"amount\" IS '金额';\n\n        COMMENT ON COLUMN \"t_user\".\"score\" IS '分数';\n\n        COMMENT ON COLUMN \"t_user\".\"creat_time\" IS '创建时间';\n\n        COMMENT ON COLUMN \"t_user\".\"describe\" IS '描述';\n\n        COMMENT ON COLUMN \"t_user\".\"uid\" IS 'uid';\n        \"\"\";\n\n\n    private static final String DB_URL = \"jdbc:gaussdb://127.0.0.1:8000/baomidou\";\n\n    private static final String DB_USER = \"root\";\n\n    private static final String DB_PASSWORD = \"123456\";\n\n    public static void main(String[] args) throws SQLException {\n        //建议使用默认的 defaultQuery() 方式查询\n//        defaultQuery();\n        sqlQuery();\n    }\n\n    private static void defaultQuery() throws SQLException {\n        DataSourceConfig dataSourceConfig = new DataSourceConfig\n            .Builder(DB_URL, DB_USER, DB_PASSWORD)\n            .databaseQueryClass(DefaultQuery.class)\n            .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {\n                //int2 这种默认是转为Short,需要转换为int单独注册自己的转换器处理适配。\n                if (metaInfo.getJdbcType().TYPE_CODE == Types.SMALLINT) {\n                    return DbColumnType.INTEGER;\n                }\n                return typeRegistry.getColumnType(metaInfo);\n            })\n            .build();\n        createTable(dataSourceConfig.getConn());\n        AutoGenerator generator = new AutoGenerator(dataSourceConfig);\n        generator.strategy(getStrategyConfig());\n        generator.execute();\n    }\n\n    private static void createTable(Connection connection) throws SQLException {\n        new SqlRunner(connection).run(CRATE_TABLE_DDL);\n    }\n\n    private static StrategyConfig getStrategyConfig() {\n        return new StrategyConfig.Builder().addInclude(\"t_user\").build()\n            .mapperBuilder().disable().serviceBuilder().disable().controllerBuilder().disable()\n            .entityBuilder().enableTableFieldAnnotation()\n            .enableFileOverride().build();\n    }\n\n    private static void sqlQuery() throws SQLException {\n        DataSourceConfig dataSourceConfig = new DataSourceConfig\n            .Builder(DB_URL, DB_USER, DB_PASSWORD)\n            .databaseQueryClass(SQLQuery.class)\n            .build();\n        createTable(dataSourceConfig.getConn());\n        AutoGenerator generator = new AutoGenerator(dataSourceConfig);\n        generator.strategy(getStrategyConfig());\n        generator.execute();\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/H2CodeGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.annotation.FieldFill;\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.*;\nimport com.baomidou.mybatisplus.generator.config.builder.CustomFile;\nimport com.baomidou.mybatisplus.generator.config.rules.DateType;\nimport com.baomidou.mybatisplus.generator.config.rules.DbColumnType;\nimport com.baomidou.mybatisplus.generator.config.rules.IColumnType;\nimport com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine;\nimport com.baomidou.mybatisplus.generator.engine.BeetlTemplateEngine;\nimport com.baomidou.mybatisplus.generator.engine.EnjoyTemplateEngine;\nimport com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;\nimport com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;\nimport com.baomidou.mybatisplus.generator.fill.Column;\nimport com.baomidou.mybatisplus.generator.fill.Property;\nimport com.baomidou.mybatisplus.generator.query.DefaultQuery;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.File;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\n/**\n * H2 代码生成\n *\n * @author hubin, lanjerry\n * @since 3.5.3\n */\npublic class H2CodeGeneratorTest extends BaseGeneratorTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(H2CodeGeneratorTest.class);\n\n    /**\n     * 执行初始化数据库脚本\n     */\n    @BeforeAll\n    public static void before() throws SQLException {\n        initDataSource(DATA_SOURCE_CONFIG);\n    }\n\n    /**\n     * 策略配置\n     */\n    public static StrategyConfig.Builder strategyConfig() {\n        return new StrategyConfig.Builder().addInclude(\"t_simple\"); // 设置需要生成的表名\n    }\n\n    private static final String H2URL = \"jdbc:h2:mem:test-h2;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;MODE=MYSQL;DATABASE_TO_LOWER=TRUE\";\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(H2URL, \"sa\", \"\")\n        // 设置SQL查询方式，默认的是元数据查询方式\n        .databaseQueryClass(DefaultQuery.class).build();\n\n    /**\n     * 简单生成\n     */\n    @Test\n    public void testSimple() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    static Stream<Arguments> testByTemplateEngine() {\n        return Stream.of(\n            Arguments.arguments(\"velocity\", new VelocityTemplateEngine()),\n            Arguments.arguments(\"freemarker\", new FreemarkerTemplateEngine()),\n            Arguments.arguments(\"beetl\", new BeetlTemplateEngine()),\n            Arguments.arguments(\"enjoy\", new EnjoyTemplateEngine())\n        );\n    }\n\n    @MethodSource\n    @ParameterizedTest\n    public void testByTemplateEngine(String engine, AbstractTemplateEngine templateEngine) {\n        String userDir = System.getProperty(\"user.dir\");\n        String outputDir = userDir + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"java\";\n        LOGGER.info(\"当前执行引擎:{}, 输出目录:{}\", engine, outputDir);\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().addTablePrefix(\"t_\")\n            .entityBuilder().enableFileOverride()\n            .mapperBuilder().enableFileOverride()\n                .controllerBuilder().enableFileOverride().serviceBuilder().enableFileOverride()\n            .build());\n        generator.packageInfo(new PackageConfig.Builder().moduleName(\"demo\" + \".\" + engine).build());\n        generator.global(globalConfig().commentDate(() -> \"2025-03-27\").disableOpenDir().outputDir(outputDir).build());\n        generator.execute(templateEngine);\n\n        // -- 生成kotlin代码\n        LOGGER.info(\"当前执行引擎:{}, 执行生成Kotlin代码.\", engine);\n        outputDir = userDir + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"kotlin\";\n        generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n\n        generator.strategy(strategyConfig().addTablePrefix(\"t_\")\n            .entityBuilder().enableFileOverride().importPackageFunction(ArrayList::new)\n            .mapperBuilder().enableFileOverride().enableBaseResultMap().importPackageFunction(ArrayList::new)\n            .controllerBuilder().enableFileOverride().serviceBuilder().enableFileOverride()\n            .build());\n        generator.packageInfo(new PackageConfig.Builder().moduleName(engine).build());\n        generator.global(globalConfig().enableKotlin().commentDate(() -> \"2025-03-27\").disableOpenDir().outputDir(outputDir).build());\n        generator.execute(templateEngine);\n    }\n\n    /**\n     * 过滤表前缀（后缀同理，支持多个）\n     * result: t_simple -> simple\n     */\n    @Test\n    public void testTablePrefix() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().addTablePrefix(\"t_\", \"c_\").build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 过滤字段后缀（前缀同理，支持多个）\n     * result: deleted_flag -> deleted\n     */\n    @Test\n    public void testFieldSuffix() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().addFieldSuffix(\"_flag\").build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 乐观锁字段设置\n     * result: 新增@Version注解\n     * 填充字段设置\n     * result: 新增@TableField(value = \"xxx\", fill = FieldFill.xxx)注解\n     */\n    @Test\n    public void testVersionAndFill() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().entityBuilder()\n            .versionColumnName(\"version\") // 基于数据库字段\n            .versionPropertyName(\"version\")// 基于模型属性\n            .addTableFills(new Column(\"create_time\", FieldFill.INSERT))    //基于数据库字段填充\n            .addTableFills(new Property(\"updateTime\", FieldFill.INSERT_UPDATE))    //基于模型属性填充\n            .build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 逻辑删除字段设置\n     * result: 新增@TableLogic注解\n     * 忽略字段设置\n     * result: 不生成\n     */\n    @Test\n    public void testLogicDeleteAndIgnoreColumn() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().entityBuilder()\n            .logicDeleteColumnName(\"deleted\") // 基于数据库字段\n            .logicDeletePropertyName(\"deleteFlag\")// 基于模型属性\n            .addIgnoreColumns(\"age\") // 基于数据库字段\n            .build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 重命名模板生成的文件名称\n     * result: TSimple -> TSimpleEntity\n     */\n    @Test\n    public void testCustomTemplateName() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig()\n            .entityBuilder().formatFileName(\"%sEntity\")\n            .mapperBuilder().formatMapperFileName(\"%sDao\").formatXmlFileName(\"%sXml\")\n            .controllerBuilder().formatFileName(\"%sAction\")\n            .serviceBuilder().formatServiceFileName(\"%sService\").formatServiceImplFileName(\"%sServiceImp\")\n            .build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 更改模板生成的文件路径\n     *\n     * @see OutputFile\n     */\n    @Test\n    public void testCustomTemplatePath() {\n        // 设置自定义路径\n        Map<OutputFile, String> pathInfo = new HashMap<>();\n        pathInfo.put(OutputFile.xml, \"D://\");\n        pathInfo.put(OutputFile.entity, \"D://entity//\");\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.packageInfo(packageConfig().pathInfo(pathInfo).build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 替换模板\n     */\n    @Test\n    public void testCustomTemplate() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig()\n            .entityBuilder().javaTemplate(\"/templates/entity1.java\")\n            .build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 测试文件覆盖\n     */\n    @Test\n    public void testFileOverride() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig()\n            // 实体文件覆盖\n            .entityBuilder().enableFileOverride()\n            // Mapper文件覆盖\n            .mapperBuilder().enableFileOverride()\n            // Service文件覆盖\n            .serviceBuilder().enableFileOverride()\n            // Controller文件覆盖\n            .controllerBuilder().enableFileOverride()\n            .build());\n        generator.execute();\n    }\n\n    /**\n     * 测试日期类型\n     */\n    @Test\n    public void testDateType() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        // 关闭其它模块生成只生成实体\n        generator.strategy(strategyConfig()\n            .controllerBuilder().disable()\n            .serviceBuilder().disable()\n            .mapperBuilder().disable()\n            .build());\n        // 日期类型强制设置为 Date 类型\n        generator.global(globalConfig().dateType(DateType.ONLY_DATE).build());\n        generator.execute();\n    }\n\n    /**\n     * 注入自定义属性\n     */\n    @Test\n    public void testCustomMap() {\n        // 设置自定义属性\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"abc\", 123);\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig()\n            .entityBuilder().javaTemplate(\"/templates/entity1.java\")\n            .build());\n        generator.injection(injectionConfig().customMap(map).build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 自定义模板（Map）\n     * key为文件名称，value为文件路径\n     */\n    @Test\n    public void testCustomFileByMap() {\n        // 设置自定义输出文件\n        Map<String, String> customFile = new HashMap<>();\n        customFile.put(\"DTO.java\", \"/templates/dto.java.vm\");\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.injection(injectionConfig().customFile(customFile).build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 自定义模板（单个文件）\n     */\n    @Test\n    public void testCustomFileBySingle() {\n        // 设置自定义输出文件\n        CustomFile customFile = new CustomFile.Builder().fileName(\"DTO.java\").templatePath(\"templates/dto.java.vm\").packageName(\"dto\").build();\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.injection(injectionConfig().customFile(customFile).build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 自定义模板（列表）\n     */\n    @Test\n    public void testCustomFileByList() {\n        // 设置自定义输出文件\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig()\n            // 禁用Controller默认模板\n            .controllerBuilder().disable()\n            .build());\n        generator.injection(injectionConfig().customFile(new ArrayList<>() {{\n            add(new CustomFile.Builder().fileName(\"DTO.java\").templatePath(\"/templates/dto.java.vm\").packageName(\"dto\").build());\n            add(new CustomFile.Builder().fileName(\"VO.java\").templatePath(\"/templates/vo.java.vm\").packageName(\"vo\").build());\n            // 通过格式化函数添加文件最后缀\n            add(new CustomFile.Builder().formatNameFunction(tableInfo -> \"Prefix\" + tableInfo.getEntityName() + \"Suffix\")\n                .fileName(\"Controller.java\").templatePath(\"/templates/controller.java.vm\").packageName(\"controller\").build());\n        }}).build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n    /**\n     * 测试内置模板路径自定义输出\n     */\n    @Test\n    public void testOutputFile() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        GlobalConfig globalConfig = globalConfig().build();\n        generator.global(globalConfig);\n        generator.strategy(strategyConfig().outputFile(((filePath, outputFile) -> {\n            File file = new File(filePath);\n            if (outputFile == OutputFile.controller) {\n                String outputDir = globalConfig.getOutputDir();\n                return new File(outputDir + File.separator + file.getName());\n            }\n            return file;\n        })).build());\n        generator.execute();\n    }\n\n    /**\n     * 测试开启生成实体时生成字段注解\n     */\n    @Test\n    public void testEnableTableFieldAnnotation() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().entityBuilder().enableTableFieldAnnotation().build());\n        generator.execute();\n    }\n\n    /**\n     * 测试开正则匹配包含的表名\n     */\n    @Test\n    public void testAddIncludeTables() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(new StrategyConfig.Builder().addInclude(\"^t_.*\").build());\n        generator.execute();\n    }\n\n    /**\n     * 测试开正则匹配排除的表名\n     */\n    @Test\n    public void testAddExcludeTables() {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(new StrategyConfig.Builder().addExclude(\n            // 排除 st 结尾的表\n            \".*st$\",\n            // 排除非 t_ 开头的表\n            \"^(?!t_).*\"\n        ).build()).execute();\n    }\n\n    /**\n     * 测试开启Boolean类型字段移除is前缀\n     */\n    @Test\n    public void testEnableRemoveIsPrefix() {\n        AutoGenerator generator = new AutoGenerator(new DataSourceConfig.Builder(H2URL, \"sa\", \"\")\n            .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {\n                IColumnType dbColumnType = typeRegistry.getColumnType(metaInfo);\n                if (dbColumnType == DbColumnType.BYTE) {\n                    // 这里按照自己的要求转换为指定类型\n                    return DbColumnType.BOOLEAN;\n                }\n                return dbColumnType;\n            }).build());\n        generator.strategy(strategyConfig().entityBuilder().enableRemoveIsPrefix().build());\n        generator.execute();\n    }\n\n    /**\n     * 自定义模板（列表）\n     */\n    @Test\n    public void testCustomFileByLambda() {\n        // 设置自定义属性\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"abc\", 118);\n        // 设置自定义输出文件\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.injection(injectionConfig()\n            .customMap(map)\n            .customFile(file ->\n                file.fileName(\"DTO.java\")\n                    .templatePath(\"/templates/dto.java.vm\")\n                    .filePath(\"D://\"))\n            .customFile(file ->\n                file.fileName(\"VO.java\")\n                    .templatePath(\"/templates/vo.java.vm\")\n                    .enableFileOverride()\n                    .filePath(\"D://\"))\n            .build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/MySQLGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n\n/**\n * MySQL 代码生成\n *\n * @author lanjerry\n * @since 3.5.3\n */\npublic class MySQLGeneratorTest extends BaseGeneratorTest {\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig\n        .Builder(\"jdbc:mysql://xxxx:3306/baomidou?serverTimezone=Asia/Shanghai\", \"root\", \"123456\")\n        .schema(\"baomidou\")\n        .build();\n\n    public static void main(String[] args) {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/OracleGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n/**\n * Oracle 代码生成\n *\n * @author lanjerry\n * @since 3.5.3\n */\npublic class OracleGeneratorTest extends BaseGeneratorTest {\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig\n        .Builder(\"jdbc:oracle:thin:@xxxx:1521:helowin\", \"system\", \"system\")\n        .schema(\"ANONYMOUS\")\n        .build();\n\n    public static void main(String[] args) {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/java/com/baomidou/mybatisplus/generator/samples/PostgreSQLGeneratorTest.java",
    "content": "package com.baomidou.mybatisplus.generator.samples;\n\nimport com.baomidou.mybatisplus.generator.AutoGenerator;\nimport com.baomidou.mybatisplus.generator.config.DataSourceConfig;\n\n/**\n * PostgreSQL 代码生成\n *\n * @author lanjerry\n * @since 3.5.3\n */\npublic class PostgreSQLGeneratorTest extends BaseGeneratorTest {\n\n    /**\n     * 数据源配置\n     */\n    private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig\n        .Builder(\"jdbc:postgresql://xxxx:5432/postgres\", \"postgres\", \"postgres\")\n        .build();\n\n    public static void main(String[] args) {\n        AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);\n        generator.strategy(strategyConfig().build());\n        generator.global(globalConfig().build());\n        generator.execute();\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/controller/SimpleController.kt",
    "content": "package com.baomidou.beetl.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/beetl/simple\")\nclass SimpleController\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/entity/Simple.kt",
    "content": "package com.baomidou.beetl.entity\n\nimport com.baomidou.mybatisplus.annotation.TableName\nimport com.baomidou.mybatisplus.annotation.IdType\nimport com.baomidou.mybatisplus.annotation.TableId\n\nimport java.time.LocalDateTime\nimport java.io.Serializable\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\nclass Simple : Serializable {\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    var id: Int? = null\n\n    /**\n     * 姓名\n     */\n    var name: String? = null\n\n    /**\n     * 年龄\n     */\n    var age: Int? = null\n\n    /**\n     * 删除标识1\n     */\n    var deleteFlag: Byte? = null\n\n    /**\n     * 删除标识2\n     */\n    var deleted: Byte? = null\n\n    /**\n     * 测试布尔类型\n     */\n    var isOk: Byte? = null\n\n    /**\n     * 版本\n     */\n    var version: Long? = null\n\n    /**\n     * 创建时间\n     */\n    var createTime: LocalDateTime? = null\n\n    /**\n     * 更新时间\n     */\n    var updateTime: LocalDateTime? = null\n\n    override fun toString(): String {\n        return \"Simple{\" +\n        \"id=\" + id +\n        \", name=\" + name +\n        \", age=\" + age +\n        \", deleteFlag=\" + deleteFlag +\n        \", deleted=\" + deleted +\n        \", isOk=\" + isOk +\n        \", version=\" + version +\n        \", createTime=\" + createTime +\n        \", updateTime=\" + updateTime +\n        \"}\"\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/mapper/SimpleMapper.kt",
    "content": "package com.baomidou.beetl.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.beetl.entity.Simple;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface SimpleMapper : BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.beetl.mapper.SimpleMapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"com.baomidou.beetl.entity.Simple\">\n        <id column=\"id\" property=\"id\" />\n        <result column=\"name\" property=\"name\" />\n        <result column=\"age\" property=\"age\" />\n        <result column=\"delete_flag\" property=\"deleteFlag\" />\n        <result column=\"deleted\" property=\"deleted\" />\n        <result column=\"is_ok\" property=\"isOk\" />\n        <result column=\"version\" property=\"version\" />\n        <result column=\"create_time\" property=\"createTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/service/ISimpleService.kt",
    "content": "package com.baomidou.beetl.service;\n\nimport com.baomidou.beetl.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface ISimpleService : IService<Simple>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/beetl/service/impl/SimpleServiceImpl.kt",
    "content": "package com.baomidou.beetl.service.impl;\n\nimport com.baomidou.beetl.entity.Simple;\nimport com.baomidou.beetl.mapper.SimpleMapper;\nimport com.baomidou.beetl.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\nopen class SimpleServiceImpl : ServiceImpl<SimpleMapper, Simple>(), ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/controller/SimpleController.kt",
    "content": "package com.baomidou.enjoy.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/enjoy/simple\")\nclass SimpleController\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/entity/Simple.kt",
    "content": "package com.baomidou.enjoy.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName\nimport com.baomidou.mybatisplus.annotation.IdType\nimport com.baomidou.mybatisplus.annotation.TableId\n\nimport java.time.LocalDateTime\nimport java.io.Serializable\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\nclass Simple : Serializable {\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    var id: Int? = null\n\n    /**\n     * 姓名\n     */\n    var name: String? = null\n\n    /**\n     * 年龄\n     */\n    var age: Int? = null\n\n    /**\n     * 删除标识1\n     */\n    var deleteFlag: Byte? = null\n\n    /**\n     * 删除标识2\n     */\n    var deleted: Byte? = null\n\n    /**\n     * 测试布尔类型\n     */\n    var isOk: Byte? = null\n\n    /**\n     * 版本\n     */\n    var version: Long? = null\n\n    /**\n     * 创建时间\n     */\n    var createTime: LocalDateTime? = null\n\n    /**\n     * 更新时间\n     */\n    var updateTime: LocalDateTime? = null\n\n    override fun toString(): String {\n        return \"Simple{\" +\n        \"id=\" + id +\n        \", name=\" + name +\n        \", age=\" + age +\n        \", deleteFlag=\" + deleteFlag +\n        \", deleted=\" + deleted +\n        \", isOk=\" + isOk +\n        \", version=\" + version +\n        \", createTime=\" + createTime +\n        \", updateTime=\" + updateTime +\n        \"}\"\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/mapper/SimpleMapper.kt",
    "content": "package com.baomidou.enjoy.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.enjoy.entity.Simple;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface SimpleMapper : BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.enjoy.mapper.SimpleMapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"com.baomidou.enjoy.entity.Simple\">\n        <id column=\"id\" property=\"id\" />\n        <result column=\"name\" property=\"name\" />\n        <result column=\"age\" property=\"age\" />\n        <result column=\"delete_flag\" property=\"deleteFlag\" />\n        <result column=\"deleted\" property=\"deleted\" />\n        <result column=\"is_ok\" property=\"isOk\" />\n        <result column=\"version\" property=\"version\" />\n        <result column=\"create_time\" property=\"createTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/service/ISimpleService.kt",
    "content": "package com.baomidou.enjoy.service;\n\nimport com.baomidou.enjoy.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface ISimpleService : IService<Simple>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/enjoy/service/impl/SimpleServiceImpl.kt",
    "content": "package com.baomidou.enjoy.service.impl;\n\nimport com.baomidou.enjoy.entity.Simple;\nimport com.baomidou.enjoy.mapper.SimpleMapper;\nimport com.baomidou.enjoy.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\nopen class SimpleServiceImpl : ServiceImpl<SimpleMapper, Simple>(), ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/controller/SimpleController.kt",
    "content": "package com.baomidou.freemarker.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/freemarker/simple\")\nclass SimpleController\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/entity/Simple.kt",
    "content": "package com.baomidou.freemarker.entity\n\nimport com.baomidou.mybatisplus.annotation.TableName\nimport com.baomidou.mybatisplus.annotation.IdType\nimport com.baomidou.mybatisplus.annotation.TableId\n\nimport java.time.LocalDateTime\nimport java.io.Serializable\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\nclass Simple : Serializable {\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    var id: Int? = null\n\n    /**\n     * 姓名\n     */\n    var name: String? = null\n\n    /**\n     * 年龄\n     */\n    var age: Int? = null\n\n    /**\n     * 删除标识1\n     */\n    var deleteFlag: Byte? = null\n\n    /**\n     * 删除标识2\n     */\n    var deleted: Byte? = null\n\n    /**\n     * 测试布尔类型\n     */\n    var isOk: Byte? = null\n\n    /**\n     * 版本\n     */\n    var version: Long? = null\n\n    /**\n     * 创建时间\n     */\n    var createTime: LocalDateTime? = null\n\n    /**\n     * 更新时间\n     */\n    var updateTime: LocalDateTime? = null\n\n    override fun toString(): String {\n        return \"Simple{\" +\n        \"id=\" + id +\n        \", name=\" + name +\n        \", age=\" + age +\n        \", deleteFlag=\" + deleteFlag +\n        \", deleted=\" + deleted +\n        \", isOk=\" + isOk +\n        \", version=\" + version +\n        \", createTime=\" + createTime +\n        \", updateTime=\" + updateTime +\n        \"}\"\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/mapper/SimpleMapper.kt",
    "content": "package com.baomidou.freemarker.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.freemarker.entity.Simple;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface SimpleMapper : BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.freemarker.mapper.SimpleMapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"com.baomidou.freemarker.entity.Simple\">\n        <id column=\"id\" property=\"id\" />\n        <result column=\"name\" property=\"name\" />\n        <result column=\"age\" property=\"age\" />\n        <result column=\"delete_flag\" property=\"deleteFlag\" />\n        <result column=\"deleted\" property=\"deleted\" />\n        <result column=\"is_ok\" property=\"isOk\" />\n        <result column=\"version\" property=\"version\" />\n        <result column=\"create_time\" property=\"createTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/service/ISimpleService.kt",
    "content": "package com.baomidou.freemarker.service;\n\nimport com.baomidou.freemarker.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface ISimpleService : IService<Simple>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/freemarker/service/impl/SimpleServiceImpl.kt",
    "content": "package com.baomidou.freemarker.service.impl;\n\nimport com.baomidou.freemarker.entity.Simple;\nimport com.baomidou.freemarker.mapper.SimpleMapper;\nimport com.baomidou.freemarker.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\nopen class SimpleServiceImpl : ServiceImpl<SimpleMapper, Simple>(), ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/controller/SimpleController.kt",
    "content": "package com.baomidou.velocity.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.stereotype.Controller;\n\n/**\n * <p>\n * 测试表 前端控制器\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Controller\n@RequestMapping(\"/velocity/simple\")\nclass SimpleController\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/entity/Simple.kt",
    "content": "package com.baomidou.velocity.entity;\n\nimport com.baomidou.mybatisplus.annotation.TableName\nimport com.baomidou.mybatisplus.annotation.IdType\nimport com.baomidou.mybatisplus.annotation.TableId\n\nimport java.time.LocalDateTime\nimport java.io.Serializable\n\n/**\n * <p>\n * 测试表\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@TableName(\"t_simple\")\nclass Simple : Serializable {\n\n    /**\n     * id\n     */\n    @TableId(value = \"id\", type = IdType.AUTO)\n    var id: Int? = null\n\n    /**\n     * 姓名\n     */\n    var name: String? = null\n\n    /**\n     * 年龄\n     */\n    var age: Int? = null\n\n    /**\n     * 删除标识1\n     */\n    var deleteFlag: Byte? = null\n\n    /**\n     * 删除标识2\n     */\n    var deleted: Byte? = null\n\n    /**\n     * 测试布尔类型\n     */\n    var isOk: Byte? = null\n\n    /**\n     * 版本\n     */\n    var version: Long? = null\n\n    /**\n     * 创建时间\n     */\n    var createTime: LocalDateTime? = null\n\n    /**\n     * 更新时间\n     */\n    var updateTime: LocalDateTime? = null\n\n    override fun toString(): String {\n        return \"Simple{\" +\n        \"id=\" + id +\n        \", name=\" + name +\n        \", age=\" + age +\n        \", deleteFlag=\" + deleteFlag +\n        \", deleted=\" + deleted +\n        \", isOk=\" + isOk +\n        \", version=\" + version +\n        \", createTime=\" + createTime +\n        \", updateTime=\" + updateTime +\n        \"}\"\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/mapper/SimpleMapper.kt",
    "content": "package com.baomidou.velocity.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.velocity.entity.Simple;\n\n/**\n * <p>\n * 测试表 Mapper 接口\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface SimpleMapper : BaseMapper<Simple> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/mapper/xml/SimpleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.baomidou.velocity.mapper.SimpleMapper\">\n\n    <!-- 通用查询映射结果 -->\n    <resultMap id=\"BaseResultMap\" type=\"com.baomidou.velocity.entity.Simple\">\n        <id column=\"id\" property=\"id\" />\n        <result column=\"name\" property=\"name\" />\n        <result column=\"age\" property=\"age\" />\n        <result column=\"delete_flag\" property=\"deleteFlag\" />\n        <result column=\"deleted\" property=\"deleted\" />\n        <result column=\"is_ok\" property=\"isOk\" />\n        <result column=\"version\" property=\"version\" />\n        <result column=\"create_time\" property=\"createTime\" />\n        <result column=\"update_time\" property=\"updateTime\" />\n    </resultMap>\n\n</mapper>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/service/ISimpleService.kt",
    "content": "package com.baomidou.velocity.service;\n\nimport com.baomidou.velocity.entity.Simple;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * <p>\n * 测试表 服务类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\ninterface ISimpleService : IService<Simple>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/kotlin/com/baomidou/velocity/service/impl/SimpleServiceImpl.kt",
    "content": "package com.baomidou.velocity.service.impl;\n\nimport com.baomidou.velocity.entity.Simple;\nimport com.baomidou.velocity.mapper.SimpleMapper;\nimport com.baomidou.velocity.service.ISimpleService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * <p>\n * 测试表 服务实现类\n * </p>\n *\n * @author baomidou\n * @since 2025-03-27\n */\n@Service\nopen class SimpleServiceImpl : ServiceImpl<SimpleMapper, Simple>(), ISimpleService {\n\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/sql/init.sql",
    "content": "drop table if exists `t_simple`;\ncreate table `t_simple`\n(\n    id          int auto_increment comment 'id',\n    name        varchar(50) comment '姓名',\n    age         int comment '年龄',\n    delete_flag tinyint(1) comment '删除标识1',\n    deleted tinyint(1) comment '删除标识2',\n    is_ok     tinyint(1) comment '测试布尔类型',\n    version     bigint comment '版本',\n    create_time datetime comment '创建时间',\n    update_time datetime comment '更新时间',\n    primary key (id)\n) COMMENT = '测试表';\ndrop table if exists `t_test`;\ncreate table `t_test`\n(\n    id          int auto_increment comment 'id',\n    name        varchar(50) comment '姓名',\n    primary key (id)\n) COMMENT = '测试表';\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/dto.java.ftl",
    "content": "package ${package.Entity};\n\n\n/**\n * <p>\n * 测试自定义DTO模板 ${table.comment!}\n * </p>\n *\n * 测试注入 ${cfg.abc}\n */\n\n\n// 自定义的查询内容\n<#list table.fields as field>\n    自定义 abc 内容： ${field.customMap.abc}\n    自定义 def 内容： ${field.customMap.def}\n</#list>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/dto.java.vm",
    "content": "package ${package.Entity};\n\n/**\n * <p>\n * 测试自定义DTO模板  $!{table.comment}\n * </p>\n * 测试注入 ${cfg.abc}\n */\n\n// 自定义的查询内容\n#foreach($field in ${table.fields})\n自定义 abc 内容： ${field.customMap.abc}\n    自定义 def 内容： ${field.customMap.def}\n#end\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/entity1.java.ftl",
    "content": "package ${package.Entity};\n\n<#list table.importPackages as pkg>\nimport ${pkg};\n</#list>\n<#if swagger>\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n</#if>\n<#if entityLombokModel>\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.Accessors;\n</#if>\n\n/**\n * <p>\n    自定义模板\n * ${table.comment!}\n    自定义属性注入abc=${cfg.abc}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n<#if entityLombokModel>\n@Data\n    <#if superEntityClass??>\n@EqualsAndHashCode(callSuper = true)\n    <#else>\n@EqualsAndHashCode(callSuper = false)\n    </#if>\n@Accessors(chain = true)\n</#if>\n<#if table.convert>\n@TableName(\"${table.name}\")\n</#if>\n<#if swagger>\n@ApiModel(value=\"${entity}对象\", description=\"${table.comment!}\")\n</#if>\n<#if superEntityClass??>\npublic class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {\n<#elseif activeRecord>\npublic class ${entity} extends Model<${entity}> {\n<#else>\npublic class ${entity} implements Serializable {\n</#if>\n\n    private static final long serialVersionUID = 1L;\n<#-- ----------  BEGIN 字段循环遍历  ---------->\n<#list table.fields as field>\n    <#if field.keyFlag>\n        <#assign keyPropertyName=\"${field.propertyName}\"/>\n    </#if>\n\n    <#if field.comment!?length gt 0>\n    <#if swagger>\n    @ApiModelProperty(value = \"${field.comment}\")\n    <#else>\n    /**\n     * ${field.comment}\n     */\n    </#if>\n    </#if>\n    <#if field.keyFlag>\n    <#-- 主键 -->\n        <#if field.keyIdentityFlag>\n    @TableId(value = \"${field.name}\", type = IdType.AUTO)\n        <#elseif idType??>\n    @TableId(value = \"${field.name}\", type = IdType.${idType})\n        <#elseif field.convert>\n    @TableId(\"${field.name}\")\n        </#if>\n    <#-- 普通字段 -->\n    <#elseif field.fill??>\n    <#-- -----   存在字段填充设置   ----->\n        <#if field.convert>\n    @TableField(value = \"${field.name}\", fill = FieldFill.${field.fill})\n        <#else>\n    @TableField(fill = FieldFill.${field.fill})\n        </#if>\n    <#elseif field.convert>\n    @TableField(\"${field.name}\")\n    </#if>\n<#-- 乐观锁注解 -->\n    <#if (versionFieldName!\"\") == field.name>\n    @Version\n    </#if>\n<#-- 逻辑删除注解 -->\n    <#if (logicDeleteFieldName!\"\") == field.name>\n    @TableLogic\n    </#if>\n    private ${field.propertyType} ${field.propertyName};\n</#list>\n<#------------  END 字段循环遍历  ---------->\n\n<#if !entityLombokModel>\n    <#list table.fields as field>\n        <#if field.propertyType == \"boolean\">\n            <#assign getprefix=\"is\"/>\n        <#else>\n            <#assign getprefix=\"get\"/>\n        </#if>\n    public ${field.propertyType} ${getprefix}${field.capitalName}() {\n        return ${field.propertyName};\n    }\n\n        <#if entityBuilderModel>\n    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n        <#else>\n    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n        </#if>\n        this.${field.propertyName} = ${field.propertyName};\n        <#if entityBuilderModel>\n        return this;\n        </#if>\n    }\n    </#list>\n</#if>\n\n<#if entityColumnConstant>\n    <#list table.fields as field>\n    public static final String ${field.name?upper_case} = \"${field.name}\";\n\n    </#list>\n</#if>\n<#if activeRecord>\n    @Override\n    protected Serializable pkVal() {\n    <#if keyPropertyName??>\n        return this.${keyPropertyName};\n    <#else>\n        return null;\n    </#if>\n    }\n\n</#if>\n<#if !entityLombokModel>\n    @Override\n    public String toString() {\n        return \"${entity}{\" +\n    <#list table.fields as field>\n        <#if field_index==0>\n        \"${field.propertyName}=\" + ${field.propertyName} +\n        <#else>\n        \", ${field.propertyName}=\" + ${field.propertyName} +\n        </#if>\n    </#list>\n        \"}\";\n    }\n</#if>\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/entity1.java.vm",
    "content": "package ${package.Entity};\n\n#foreach($pkg in ${table.importPackages})\nimport ${pkg};\n#end\n#if(${swagger})\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n#end\n#if(${entityLombokModel})\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.Accessors;\n#end\n\n/**\n * <p>\n * 自定义模板entity\n * 自定义属性注入abc=$!{abc}\n * $!{table.comment}\n * </p>\n *\n * @author ${author}\n * @since ${date}\n */\n#if(${entityLombokModel})\n@Data\n#if(${superEntityClass})\n@EqualsAndHashCode(callSuper = true)\n#else\n@EqualsAndHashCode(callSuper = false)\n#end\n@Accessors(chain = true)\n#end\n#if(${table.convert})\n@TableName(\"${table.name}\")\n#end\n#if(${swagger})\n@ApiModel(value=\"${entity}对象\", description=\"$!{table.comment}\")\n#end\n#if(${superEntityClass})\npublic class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {\n#elseif(${activeRecord})\npublic class ${entity} extends Model<${entity}> {\n#else\npublic class ${entity} implements Serializable {\n#end\n\n    private static final long serialVersionUID = 1L;\n## ----------  BEGIN 字段循环遍历  ----------\n#foreach($field in ${table.fields})\n\n#if(${field.keyFlag})\n#set($keyPropertyName=${field.propertyName})\n#end\n#if(\"$!field.comment\" != \"\")\n#if(${swagger})\n    @ApiModelProperty(value = \"${field.comment}\")\n#else\n    /**\n     * ${field.comment}\n     */\n#end\n#end\n#if(${field.keyFlag})\n## 主键\n#if(${field.keyIdentityFlag})\n    @TableId(value = \"${field.name}\", type = IdType.AUTO)\n#elseif(!$null.isNull(${idType}) && \"$!idType\" != \"\")\n    @TableId(value = \"${field.name}\", type = IdType.${idType})\n#elseif(${field.convert})\n    @TableId(\"${field.name}\")\n#end\n## 普通字段\n#elseif(${field.fill})\n## -----   存在字段填充设置   -----\n#if(${field.convert})\n    @TableField(value = \"${field.name}\", fill = FieldFill.${field.fill})\n#else\n    @TableField(fill = FieldFill.${field.fill})\n#end\n#elseif(${field.convert})\n    @TableField(\"${field.name}\")\n#end\n## 乐观锁注解\n#if(${versionFieldName}==${field.name})\n    @Version\n#end\n## 逻辑删除注解\n#if(${logicDeleteFieldName}==${field.name})\n    @TableLogic\n#end\n    private ${field.propertyType} ${field.propertyName};\n#end\n## ----------  END 字段循环遍历  ----------\n\n#if(!${entityLombokModel})\n#foreach($field in ${table.fields})\n#if(${field.propertyType.equals(\"boolean\")})\n#set($getprefix=\"is\")\n#else\n#set($getprefix=\"get\")\n#end\n\n    public ${field.propertyType} ${getprefix}${field.capitalName}() {\n        return ${field.propertyName};\n    }\n\n#if(${entityBuilderModel})\n    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n#else\n    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {\n#end\n        this.${field.propertyName} = ${field.propertyName};\n#if(${entityBuilderModel})\n        return this;\n#end\n    }\n#end\n#end\n\n#if(${entityColumnConstant})\n#foreach($field in ${table.fields})\n    public static final String ${field.name.toUpperCase()} = \"${field.name}\";\n\n#end\n#end\n#if(${activeRecord})\n    @Override\n    protected Serializable pkVal() {\n#if(${keyPropertyName})\n        return this.${keyPropertyName};\n#else\n        return null;\n#end\n    }\n\n#end\n#if(!${entityLombokModel})\n    @Override\n    public String toString() {\n        return \"${entity}{\" +\n#foreach($field in ${table.fields})\n#if($!{foreach.index}==0)\n        \"${field.propertyName}=\" + ${field.propertyName} +\n#else\n        \", ${field.propertyName}=\" + ${field.propertyName} +\n#end\n#end\n        \"}\";\n    }\n#end\n}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/test.vm",
    "content": "hello,I am test file---${entity}\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/vo.java.ftl",
    "content": "package ${package.Entity};\n\n\n/**\n * <p>\n * 测试自定义VO模板 ${table.comment!}\n * </p>\n *\n * 测试注入 ${cfg.abc}\n */\n\n\n// 自定义的查询内容\n<#list table.fields as field>\n    字段： ${field.propertyName}\n</#list>\n"
  },
  {
    "path": "mybatis-plus-generator/src/test/resources/templates/vo.java.vm",
    "content": "package ${package.Entity};\n\n/**\n * <p>\n * 测试自定义VO模板  $!{table.comment}\n * </p>\n * 测试注入 ${abc}\n */\n\n// 自定义的查询内容\n#foreach($field in ${table.fields})\n字段： ${field.propertyName}\n#end\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/build.gradle",
    "content": "tasks.matching {it.group == 'publishing' || it.group == 'central publish' }.each { it.enabled = false }\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/build.gradle",
    "content": "compileJava {\n    options.release = 11\n}\n\ndependencies {\n    api \"com.github.jsqlparser:jsqlparser:5.2\"\n    api project(\":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-common\")\n    implementation \"${lib.\"slf4j-api\"}\"\n    implementation \"de.ruedigermoeller:fst:3.0.3\"\n    implementation \"org.apache.fury:fury-core:0.9.0\"\n    implementation \"com.github.ben-manes.caffeine:caffeine:2.9.3\"\n    testImplementation \"io.github.classgraph:classgraph:4.8.177\"\n    testImplementation \"${lib.\"spring-context-support\"}\"\n    testImplementation \"${lib.h2}\"\n    testImplementation group: 'com.google.guava', name: 'guava', version: '33.3.1-jre'\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/DynamicTableNameHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.create.index.CreateIndex;\nimport net.sf.jsqlparser.statement.create.view.CreateView;\nimport net.sf.jsqlparser.statement.drop.Drop;\nimport net.sf.jsqlparser.util.TablesNamesFinder;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 动态表名解析处理\n * <p>1.无法保留sql注释(例如 select * from test; --这是个查询 处理完会变成 select * from test)</p>\n * <p>2.无法保留语句分隔符;(例如 select * from test; 处理完会变成 select * from test )</p>\n * <p>3.如果使用转义符包裹了表名需要自行处理</p>\n * <p>4.select * from dual (不处理这个,自行忽略)</p>\n *\n * @author nieqiurong\n * @since 3.5.11\n */\npublic class DynamicTableNameHandler extends TablesNamesFinder<Void> {\n\n    private final String originSql;\n\n    private final TableNameHandler tableNameHandler;\n\n    private final Set<Table> set = new HashSet<>();\n\n    public DynamicTableNameHandler(String originSql, TableNameHandler tableNameHandler) {\n        this.originSql = originSql;\n        this.tableNameHandler = tableNameHandler;\n        init(false);\n    }\n\n    @Override\n    public <S> Void visit(CreateIndex createIndex, S context) {\n        return this.visit(createIndex.getTable(), context);\n    }\n\n    @Override\n    public <S> Void visit(CreateView create, S context) {\n        return this.visit(create.getSelect(), context);\n    }\n\n    @Override\n    public <S> Void visit(Drop drop, S context) {\n        if(StringUtils.isNotBlank(drop.getType())){\n            String type = drop.getType().toUpperCase();\n            if (\"TABLE\".equals(type)) {\n                return super.visit(drop, context);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    protected String extractTableName(Table table) {\n        String originalTableName = table.getName();\n        if (table.getASTNode() == null) {\n            return originalTableName;\n        }\n        if (set.add(table)) {\n            String tableName = tableNameHandler.dynamicTableName(originSql, originalTableName);\n            if (StringUtils.isNotBlank(tableName)) {\n                table.setName(tableName);\n                return tableName;\n            }\n        }\n        return originalTableName;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport net.sf.jsqlparser.JSQLParserException;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\n@FunctionalInterface\npublic interface JsqlParserFunction<T, R> {\n\n    R apply(T t) throws JSQLParserException;\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.JsqlParseCache;\nimport com.baomidou.mybatisplus.jsqlparser.JsqlParserThreadPool;\nimport lombok.Setter;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\nimport java.util.concurrent.ExecutorService;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\npublic class JsqlParserGlobal {\n\n\n    /**\n     * 默认线程数大小\n     *\n     * @since 3.5.6\n     * @deprecated {@link JsqlParserThreadPool#DEFAULT_THREAD_SIZE}\n     */\n    @Deprecated\n    public static final int DEFAULT_THREAD_SIZE = (Runtime.getRuntime().availableProcessors() + 1) / 2;\n\n    /**\n     * 默认解析处理线程池\n     * <p>注意: 由于项目情况,机器配置等不一样因素,请自行根据情况创建指定线程池.</p>\n     *\n     * @see java.util.concurrent.ThreadPoolExecutor\n     * @see #setExecutorService(ExecutorService)\n     * @see #setExecutorService(ExecutorService, boolean)\n     * @since 3.5.6\n     * @deprecated 3.5.11 后面不再公开此属性\n     */\n    @Deprecated\n    public static ExecutorService executorService;\n\n    @Setter\n    private static JsqlParserFunction<String, Statement> parserSingleFunc = sql -> CCJSqlParserUtil.parse(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParserFunction<String, Statements> parserMultiFunc = sql -> CCJSqlParserUtil.parseStatements(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParseCache jsqlParseCache;\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @since 3.5.11\n     */\n    public static void setExecutorService(ExecutorService executorService) {\n        JsqlParserGlobal.executorService = executorService;\n    }\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param addShutdownHook 是否注册退出关闭钩子\n     * @since 3.5.11\n     * @deprecated 3.5.12 推荐使用 {@link #setExecutorService(ExecutorService, Thread)}\n     */\n    @Deprecated\n    public static void setExecutorService(ExecutorService executorService, boolean addShutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (addShutdownHook) {\n            JsqlParserThreadPool.addShutdownHook(executorService);\n        }\n    }\n\n    /**\n     * 设置解析线程池\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param shutdownHook 关闭钩子\n     * @since 3.5.12\n     */\n    public static void setExecutorService(ExecutorService executorService, Thread shutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (shutdownHook != null) {\n            Runtime.getRuntime().addShutdownHook(shutdownHook);\n        }\n    }\n\n    /**\n     * 获取解析线程池(如果未自定义则返回默认的解析线程池)\n     *\n     * @return 解析线程池\n     * @since 3.5.11\n     */\n    public static ExecutorService getExecutorService() {\n        return JsqlParserGlobal.executorService == null ? JsqlParserThreadPool.getDefaultThreadPoolExecutor() : JsqlParserGlobal.executorService;\n    }\n\n    public static Statement parse(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserSingleFunc.apply(sql);\n        }\n        Statement statement = jsqlParseCache.getStatement(sql);\n        if (statement == null) {\n            statement = parserSingleFunc.apply(sql);\n            jsqlParseCache.putStatement(sql, statement);\n        }\n        return statement;\n    }\n\n    public static Statements parseStatements(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserMultiFunc.apply(sql);\n        }\n        Statements statements = jsqlParseCache.getStatements(sql);\n        if (statements == null) {\n            statements = parserMultiFunc.apply(sql);\n            jsqlParseCache.putStatements(sql, statements);\n        }\n        return statements;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\n/**\n * https://github.com/JSQLParser/JSqlParser\n *\n * @author miemie\n * @since 2020-06-22\n */\npublic abstract class JsqlParserSupport {\n\n    /**\n     * 日志\n     */\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n    public String parserSingle(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            return processParser(statement, 0, sql, obj);\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    public String parserMulti(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            // fixed github pull/295\n            StringBuilder sb = new StringBuilder();\n            Statements statements = JsqlParserGlobal.parseStatements(sql);\n            int i = 0;\n            for (Statement statement : statements) {\n                if (i > 0) {\n                    sb.append(StringPool.SEMICOLON);\n                }\n                sb.append(processParser(statement, i, sql, obj));\n                i++;\n            }\n            return sb.toString();\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    /**\n     * 执行 SQL 解析\n     *\n     * @param statement JsqlParser Statement\n     * @return sql\n     */\n    protected String processParser(Statement statement, int index, String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"SQL to parse, SQL: \" + sql);\n        }\n        if (statement instanceof Insert) {\n            this.processInsert((Insert) statement, index, sql, obj);\n        } else if (statement instanceof Select) {\n            this.processSelect((Select) statement, index, sql, obj);\n        } else if (statement instanceof Update) {\n            this.processUpdate((Update) statement, index, sql, obj);\n        } else if (statement instanceof Delete) {\n            this.processDelete((Delete) statement, index, sql, obj);\n        }\n        sql = statement.toString();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"parse the finished SQL: \" + sql);\n        }\n        return sql;\n    }\n\n    /**\n     * 新增\n     */\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 删除\n     */\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 更新\n     */\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 查询\n     */\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Executor;\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Caffeine 缓存实现抽象类\n *\n * @author miemie hubin\n * @since 2023-08-08\n */\npublic abstract class AbstractCaffeineJsqlParseCache implements JsqlParseCache {\n    protected final Log logger = LogFactory.getLog(this.getClass());\n    protected final Cache<String, byte[]> cache;\n    @Setter\n    protected boolean async = false;\n    @Setter\n    protected Executor executor;\n\n    public AbstractCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        this.cache = cache;\n    }\n\n    public AbstractCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        Caffeine<Object, Object> caffeine = Caffeine.newBuilder();\n        consumer.accept(caffeine);\n        this.cache = caffeine.build();\n    }\n\n    @Override\n    public void putStatement(String sql, Statement value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public void putStatements(String sql, Statements value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public Statement getStatement(String sql) {\n        return this.get(sql);\n    }\n\n    @Override\n    public Statements getStatements(String sql) {\n        return this.get(sql);\n    }\n\n    /**\n     * 获取解析对象，异常清空缓存逻辑\n     *\n     * @param sql 执行 SQL\n     * @return 返回泛型对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T get(String sql) {\n        byte[] bytes = cache.getIfPresent(sql);\n        if (null != bytes) {\n            try {\n                return (T) deserialize(sql, bytes);\n            } catch (Exception e) {\n                cache.invalidate(sql);\n                logger.error(\"deserialize error\", e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 存储解析对象\n     *\n     * @param sql   执行 SQL\n     * @param value 解析对象\n     */\n    protected void put(String sql, Object value) {\n        final byte[] serialVal = serialize(value);\n        if (async) {\n            if (executor != null) {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal), executor);\n            } else {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal));\n            }\n        } else {\n            cache.put(sql, serialVal);\n        }\n    }\n\n    /**\n     * 序列化\n     */\n    public abstract byte[] serialize(Object obj);\n\n    /**\n     * 反序列化\n     */\n    public abstract Object deserialize(String sql, byte[] bytes);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport org.nustaq.serialization.FSTConfiguration;\n\n/**\n * Fst Factory\n *\n * @author miemie\n * @since 2023-08-06\n */\npublic class FstFactory {\n    private static final FstFactory FACTORY = new FstFactory();\n    private final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();\n\n    public static FstFactory getDefaultFactory() {\n        return FACTORY;\n    }\n\n    public FstFactory() {\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.AliasColumn.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AllValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnalyticExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnyComparisonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.BooleanValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CaseExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CastExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CollateExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ConnectByPriorOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ConnectByRootOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateTimeLiteralExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DoubleValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ExtractExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.FilterOverImpl.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Function.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Function.HavingClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.HexValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.HighExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.IntervalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Inverse.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcNamedParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonAggregateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunctionExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonKeyValuePair.class);\n        conf.registerClass(net.sf.jsqlparser.expression.KeepExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LambdaExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LongValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LowExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLGroupConcat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLIndexHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NextValExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NotExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NullValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NumericBind.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHierarchicalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleNamedFunctionParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OrderByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OverlapsCondition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Parenthesis.class);\n        conf.registerClass(net.sf.jsqlparser.expression.PartitionByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.PreferringClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RangeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowGetExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SQLServerHints.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SignedExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.StringValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.StructType.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeKeyExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimestampValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimezoneExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TranscodingFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TrimFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.UserVariable.class);\n        conf.registerClass(net.sf.jsqlparser.expression.VariableAssignment.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WhenClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowElement.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowOffset.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowRange.class);\n        conf.registerClass(net.sf.jsqlparser.expression.XMLSerializeExpr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Addition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Concat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Division.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Modulo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Multiplication.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Subtraction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.AndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.OrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.XorExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Between.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ContainedBy.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Contains.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.CosineSimilarity.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.DoubleAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.EqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExcludesExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExistsExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.FullTextSearch.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GeometryDistance.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.InExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IncludesExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsNullExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.JsonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.LikeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Matches.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MemberOfExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NamedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NotEqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Plus.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.PriorTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.SimilarToExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin.class);\n        conf.registerClass(net.sf.jsqlparser.parser.ASTNodeAccessImpl.class);\n        conf.registerClass(net.sf.jsqlparser.parser.Token.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Column.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Sequence.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Synonym.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Table.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Block.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Commit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.TypeDefExpr.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DescribeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.Option.class);\n        conf.registerClass(net.sf.jsqlparser.statement.IfElseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.OutputClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.PurgeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ReferentialAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ResetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.RollbackStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SavepointStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowColumnsStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Statements.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UnsupportedStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.Alter.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropDefault.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetDefault.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetVisibility.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSession.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSystemStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.RenameTableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.sequence.AlterSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.analyze.Analyze.class);\n        conf.registerClass(net.sf.jsqlparser.statement.comment.Comment.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.function.CreateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.index.CreateIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.procedure.CreateProcedure.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.schema.CreateSchema.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.sequence.CreateSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.synonym.CreateSynonym.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CheckConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColumnDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CreateTable.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ExcludeConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.ColumnParams.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.NamedConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.PartitionDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.RowMovement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.AlterView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.CreateView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.delete.Delete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.delete.ParenthesedDelete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.drop.Drop.class);\n        conf.registerClass(net.sf.jsqlparser.statement.execute.Execute.class);\n        conf.registerClass(net.sf.jsqlparser.statement.grant.Grant.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.Insert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictTarget.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.ParenthesedInsert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.Merge.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeDelete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeInsert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeUpdate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.AggregatePipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.AsPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.CallPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.DropPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.ExtendPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.FromQuery.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.JoinPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.LimitPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.OrderByPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.PivotPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.RenamePipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.SelectPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.SetOperationPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.SetPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.TableSamplePipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.UnPivotPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.WherePipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.WindowPipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllTableColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Distinct.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ExceptOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Fetch.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.First.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ForClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.FunctionAllColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.GroupByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.IntersectOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Join.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLJoinWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralSubSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Limit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.MinusOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Offset.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OptimizeFor.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OrderByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedFromItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Pivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PivotXml.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PlainSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SelectItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperationList.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Skip.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Top.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnPivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnionOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Values.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Wait.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithIsolation.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowIndexStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowTablesStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.truncate.Truncate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.ParenthesedUpdate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.Update.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.UpdateSet.class);\n        conf.registerClass(net.sf.jsqlparser.statement.upsert.Upsert.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiAndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiOrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.BinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ComparisonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.CreateFunctionalStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.piped.PipeOperator.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Select.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperation.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultipleExpression.class);\n    }\n\n    public byte[] asByteArray(Object obj) {\n        return conf.asByteArray(obj);\n    }\n\n    public Object asObject(byte[] bytes) {\n        return conf.asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Fst 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class FstSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public FstSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public FstSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return FstFactory.getDefaultFactory().asByteArray(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return FstFactory.getDefaultFactory().asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FuryFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport org.apache.fury.Fury;\nimport org.apache.fury.ThreadSafeFury;\n\n/**\n * Fury Factory.\n *\n * @author laokou\n */\npublic final class FuryFactory {\n\n    private static final FuryFactory FACTORY = new FuryFactory();\n\n    private final ThreadSafeFury FURY = Fury.builder()\n        // 开启异步编译\n        .withAsyncCompilation(true)\n        .buildThreadSafeFury();\n\n    public FuryFactory() {\n        FURY.register(net.sf.jsqlparser.expression.Alias.class);\n        FURY.register(net.sf.jsqlparser.expression.Alias.AliasColumn.class);\n        FURY.register(net.sf.jsqlparser.expression.AllValue.class);\n        FURY.register(net.sf.jsqlparser.expression.AnalyticExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.AnyComparisonExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.ArrayConstructor.class);\n        FURY.register(net.sf.jsqlparser.expression.ArrayExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.CaseExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.CastExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.CollateExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.ConnectByRootOperator.class);\n        FURY.register(net.sf.jsqlparser.expression.DateTimeLiteralExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.DateValue.class);\n        FURY.register(net.sf.jsqlparser.expression.DoubleValue.class);\n        FURY.register(net.sf.jsqlparser.expression.ExtractExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.FilterOverImpl.class);\n        FURY.register(net.sf.jsqlparser.expression.Function.class);\n        FURY.register(net.sf.jsqlparser.expression.HexValue.class);\n        FURY.register(net.sf.jsqlparser.expression.IntervalExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.JdbcNamedParameter.class);\n        FURY.register(net.sf.jsqlparser.expression.JdbcParameter.class);\n        FURY.register(net.sf.jsqlparser.expression.JsonAggregateFunction.class);\n        FURY.register(net.sf.jsqlparser.expression.JsonExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.JsonFunction.class);\n        FURY.register(net.sf.jsqlparser.expression.JsonFunctionExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.JsonKeyValuePair.class);\n        FURY.register(net.sf.jsqlparser.expression.KeepExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.LongValue.class);\n        FURY.register(net.sf.jsqlparser.expression.MySQLGroupConcat.class);\n        FURY.register(net.sf.jsqlparser.expression.MySQLIndexHint.class);\n        FURY.register(net.sf.jsqlparser.expression.NextValExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.NotExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.NullValue.class);\n        FURY.register(net.sf.jsqlparser.expression.NumericBind.class);\n        FURY.register(net.sf.jsqlparser.expression.OracleHierarchicalExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.OracleHint.class);\n        FURY.register(net.sf.jsqlparser.expression.OracleNamedFunctionParameter.class);\n        FURY.register(net.sf.jsqlparser.expression.OrderByClause.class);\n        FURY.register(net.sf.jsqlparser.expression.OverlapsCondition.class);\n        FURY.register(net.sf.jsqlparser.expression.PartitionByClause.class);\n        FURY.register(net.sf.jsqlparser.expression.RangeExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.RowConstructor.class);\n        FURY.register(net.sf.jsqlparser.expression.RowGetExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.SQLServerHints.class);\n        FURY.register(net.sf.jsqlparser.expression.SignedExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.StringValue.class);\n        FURY.register(net.sf.jsqlparser.expression.TimeKeyExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.TimeValue.class);\n        FURY.register(net.sf.jsqlparser.expression.TimestampValue.class);\n        FURY.register(net.sf.jsqlparser.expression.TimezoneExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.TranscodingFunction.class);\n        FURY.register(net.sf.jsqlparser.expression.TrimFunction.class);\n        FURY.register(net.sf.jsqlparser.expression.UserVariable.class);\n        FURY.register(net.sf.jsqlparser.expression.VariableAssignment.class);\n        FURY.register(net.sf.jsqlparser.expression.WhenClause.class);\n        FURY.register(net.sf.jsqlparser.expression.WindowDefinition.class);\n        FURY.register(net.sf.jsqlparser.expression.WindowElement.class);\n        FURY.register(net.sf.jsqlparser.expression.WindowOffset.class);\n        FURY.register(net.sf.jsqlparser.expression.WindowRange.class);\n        FURY.register(net.sf.jsqlparser.expression.XMLSerializeExpr.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Addition.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Concat.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Division.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Modulo.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Multiplication.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.arithmetic.Subtraction.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.conditional.AndExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.conditional.OrExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.conditional.XorExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.Between.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.ContainedBy.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.Contains.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.DoubleAnd.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.EqualsTo.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.ExistsExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.ExpressionList.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.FullTextSearch.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.GeometryDistance.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.GreaterThan.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.InExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.IsNullExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.JsonOperator.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.LikeExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.Matches.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.MemberOfExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.MinorThan.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.MinorThanEquals.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.NamedExpressionList.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.NotEqualsTo.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.SimilarToExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin.class);\n        FURY.register(net.sf.jsqlparser.parser.ASTNodeAccessImpl.class);\n        FURY.register(net.sf.jsqlparser.parser.Token.class);\n        FURY.register(net.sf.jsqlparser.schema.Column.class);\n        FURY.register(net.sf.jsqlparser.schema.Sequence.class);\n        FURY.register(net.sf.jsqlparser.schema.Synonym.class);\n        FURY.register(net.sf.jsqlparser.schema.Table.class);\n        FURY.register(net.sf.jsqlparser.statement.Block.class);\n        FURY.register(net.sf.jsqlparser.statement.Commit.class);\n        FURY.register(net.sf.jsqlparser.statement.DeclareStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.DeclareStatement.TypeDefExpr.class);\n        FURY.register(net.sf.jsqlparser.statement.DescribeStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.ExplainStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.ExplainStatement.Option.class);\n        FURY.register(net.sf.jsqlparser.statement.IfElseStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.OutputClause.class);\n        FURY.register(net.sf.jsqlparser.statement.PurgeStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.ReferentialAction.class);\n        FURY.register(net.sf.jsqlparser.statement.ResetStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.RollbackStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.SavepointStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.SetStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.ShowColumnsStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.ShowStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.Statements.class);\n        FURY.register(net.sf.jsqlparser.statement.UnsupportedStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.UseStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.Alter.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterExpression.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropDefault.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterSession.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.AlterSystemStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.RenameTableStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.alter.sequence.AlterSequence.class);\n        FURY.register(net.sf.jsqlparser.statement.analyze.Analyze.class);\n        FURY.register(net.sf.jsqlparser.statement.comment.Comment.class);\n        FURY.register(net.sf.jsqlparser.statement.create.function.CreateFunction.class);\n        FURY.register(net.sf.jsqlparser.statement.create.index.CreateIndex.class);\n        FURY.register(net.sf.jsqlparser.statement.create.procedure.CreateProcedure.class);\n        FURY.register(net.sf.jsqlparser.statement.create.schema.CreateSchema.class);\n        FURY.register(net.sf.jsqlparser.statement.create.sequence.CreateSequence.class);\n        FURY.register(net.sf.jsqlparser.statement.create.synonym.CreateSynonym.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.CheckConstraint.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.ColDataType.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.ColumnDefinition.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.CreateTable.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.ExcludeConstraint.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.Index.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.Index.ColumnParams.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.NamedConstraint.class);\n        FURY.register(net.sf.jsqlparser.statement.create.table.RowMovement.class);\n        FURY.register(net.sf.jsqlparser.statement.create.view.AlterView.class);\n        FURY.register(net.sf.jsqlparser.statement.create.view.CreateView.class);\n        FURY.register(net.sf.jsqlparser.statement.delete.Delete.class);\n        FURY.register(net.sf.jsqlparser.statement.drop.Drop.class);\n        FURY.register(net.sf.jsqlparser.statement.execute.Execute.class);\n        FURY.register(net.sf.jsqlparser.statement.grant.Grant.class);\n        FURY.register(net.sf.jsqlparser.statement.insert.Insert.class);\n        FURY.register(net.sf.jsqlparser.statement.insert.InsertConflictAction.class);\n        FURY.register(net.sf.jsqlparser.statement.insert.InsertConflictTarget.class);\n        FURY.register(net.sf.jsqlparser.statement.merge.Merge.class);\n        FURY.register(net.sf.jsqlparser.statement.merge.MergeDelete.class);\n        FURY.register(net.sf.jsqlparser.statement.merge.MergeInsert.class);\n        FURY.register(net.sf.jsqlparser.statement.merge.MergeUpdate.class);\n        FURY.register(net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.select.AllColumns.class);\n        FURY.register(net.sf.jsqlparser.statement.select.AllTableColumns.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Distinct.class);\n        FURY.register(net.sf.jsqlparser.statement.select.ExceptOp.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Fetch.class);\n        FURY.register(net.sf.jsqlparser.statement.select.First.class);\n        FURY.register(net.sf.jsqlparser.statement.select.ForClause.class);\n        FURY.register(net.sf.jsqlparser.statement.select.GroupByElement.class);\n        FURY.register(net.sf.jsqlparser.statement.select.IntersectOp.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Join.class);\n        FURY.register(net.sf.jsqlparser.statement.select.KSQLJoinWindow.class);\n        FURY.register(net.sf.jsqlparser.statement.select.KSQLWindow.class);\n        FURY.register(net.sf.jsqlparser.statement.select.LateralSubSelect.class);\n        FURY.register(net.sf.jsqlparser.statement.select.LateralView.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Limit.class);\n        FURY.register(net.sf.jsqlparser.statement.select.MinusOp.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Offset.class);\n        FURY.register(net.sf.jsqlparser.statement.select.OptimizeFor.class);\n        FURY.register(net.sf.jsqlparser.statement.select.OrderByElement.class);\n        FURY.register(net.sf.jsqlparser.statement.select.ParenthesedFromItem.class);\n        FURY.register(net.sf.jsqlparser.statement.select.ParenthesedSelect.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Pivot.class);\n        FURY.register(net.sf.jsqlparser.statement.select.PivotXml.class);\n        FURY.register(net.sf.jsqlparser.statement.select.PlainSelect.class);\n        FURY.register(net.sf.jsqlparser.statement.select.SelectItem.class);\n        FURY.register(net.sf.jsqlparser.statement.select.SetOperationList.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Skip.class);\n        FURY.register(net.sf.jsqlparser.statement.select.TableFunction.class);\n        FURY.register(net.sf.jsqlparser.statement.select.TableStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Top.class);\n        FURY.register(net.sf.jsqlparser.statement.select.UnPivot.class);\n        FURY.register(net.sf.jsqlparser.statement.select.UnionOp.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Values.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Wait.class);\n        FURY.register(net.sf.jsqlparser.statement.select.WithIsolation.class);\n        FURY.register(net.sf.jsqlparser.statement.select.WithItem.class);\n        FURY.register(net.sf.jsqlparser.statement.show.ShowIndexStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.show.ShowTablesStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.truncate.Truncate.class);\n        FURY.register(net.sf.jsqlparser.statement.update.Update.class);\n        FURY.register(net.sf.jsqlparser.statement.update.UpdateSet.class);\n        FURY.register(net.sf.jsqlparser.statement.upsert.Upsert.class);\n        FURY.register(net.sf.jsqlparser.util.cnfexpression.MultiAndExpression.class);\n        FURY.register(net.sf.jsqlparser.util.cnfexpression.MultiOrExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.BinaryExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.ComparisonOperator.class);\n        FURY.register(net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression.class);\n        FURY.register(net.sf.jsqlparser.expression.Function.NullHandling.class);\n        FURY.register(net.sf.jsqlparser.statement.CreateFunctionalStatement.class);\n        FURY.register(net.sf.jsqlparser.statement.select.Select.class);\n        FURY.register(net.sf.jsqlparser.statement.select.SetOperation.class);\n        FURY.register(net.sf.jsqlparser.util.cnfexpression.MultipleExpression.class);\n        FURY.register(net.sf.jsqlparser.statement.insert.InsertModifierPriority.class);\n        FURY.register(net.sf.jsqlparser.statement.select.OrderByElement.NullOrdering.class);\n        FURY.register(net.sf.jsqlparser.statement.select.ForMode.class);\n        FURY.register(net.sf.jsqlparser.statement.select.MySqlSqlCacheFlags.class);\n        FURY.register(net.sf.jsqlparser.statement.select.PlainSelect.BigQuerySelectQualifier.class);\n        FURY.register(net.sf.jsqlparser.statement.update.UpdateModifierPriority.class);\n    }\n\n    public static FuryFactory getFuryFactory() {\n        return FACTORY;\n    }\n\n    public byte[] serialize(Object object) {\n        if (object == null) {\n            return new byte[0];\n        }\n        return FURY.serialize(object);\n    }\n\n    public Object deserialize(byte[] bytes) {\n        if (bytes == null) {\n            return null;\n        }\n        return FURY.deserialize(bytes);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FurySerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 fury 序列化 Caffeine 缓存实现.\n *\n * @author laokou\n */\npublic class FurySerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n    public FurySerialCaffeineJsqlParseCache(Cache<String, byte[]>cache) {\n        super(cache);\n    }\n\n    public FurySerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return FuryFactory.getFuryFactory().serialize(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return FuryFactory.getFuryFactory().deserialize(bytes);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.baomidou.mybatisplus.core.toolkit.SerializationUtils;\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 jdk 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class JdkSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public JdkSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public JdkSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return SerializationUtils.serialize(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return SerializationUtils.deserialize(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\n/**\n * jsqlparser 缓存接口\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic interface JsqlParseCache {\n\n    void putStatement(String sql, Statement value);\n\n    void putStatements(String sql, Statements value);\n\n    Statement getStatement(String sql);\n\n    Statements getStatements(String sql);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.4.1 +\n */\npublic interface DataPermissionHandler {\n\n    /**\n     * 获取数据权限 SQL 片段\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会覆盖原有的条件表达式\n     */\n    Expression getSqlSegment(Expression where, String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\n\n/**\n * 支持多表的数据权限处理器\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic interface MultiDataPermissionHandler extends DataPermissionHandler {\n    /**\n     * 为兼容旧版数据权限处理器，继承了 {@link DataPermissionHandler} 但是新的多表数据权限处理又不会调用此方法，因此标记过时\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式\n     * @deprecated 新的多表数据权限处理不会调用此方法，因此标记过时\n     */\n    @Deprecated\n    @Override\n    default Expression getSqlSegment(Expression where, String mappedStatementId) {\n        return where;\n    }\n\n    /**\n     * 获取数据权限 SQL 片段。\n     * <p>旧的 {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} 方法第一个参数包含所有的 where 条件信息，如果 return 了 null 会覆盖原有的 where 数据，</p>\n     * <p>新版的 {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆盖原有的 where 数据，如果 return 了 null 则表示不追加任何 where 条件</p>\n     *\n     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名\n     * @param where             原有的 where 条件信息\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）\n     */\n    Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\n\nimport java.util.List;\n\n/**\n * 租户处理器（ TenantId 行级 ）\n *\n * @author hubin\n * @since 3.4.0\n */\npublic interface TenantLineHandler {\n\n    /**\n     * 获取租户 ID 值表达式，只支持单个 ID 值\n     * <p>\n     *\n     * @return 租户 ID 值表达式\n     */\n    Expression getTenantId();\n\n    /**\n     * 获取租户字段名\n     * <p>\n     * 默认字段名叫: tenant_id\n     *\n     * @return 租户字段名\n     */\n    default String getTenantIdColumn() {\n        return \"tenant_id\";\n    }\n\n    /**\n     * 根据表名判断是否忽略拼接多租户条件\n     * <p>\n     * 默认都要进行解析并拼接多租户条件\n     *\n     * @param tableName 表名\n     * @return 是否忽略, true:表示忽略，false:需要解析并拼接多租户条件\n     */\n    default boolean ignoreTable(String tableName) {\n        return false;\n    }\n\n    /**\n     * 忽略插入租户字段逻辑\n     *\n     * @param columns        插入字段\n     * @param tenantIdColumn 租户 ID 字段\n     * @return\n     */\n    default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {\n        return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.*;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExistsExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 多表条件处理基对象，从原有的 {@link TenantLineInnerInterceptor} 拦截器中提取出来\n *\n * @author houkunlin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 条件表达式追加模式 (默认放置最后,仅作用于update,delete,select)\n     *\n     * @since 3.5.11\n     */\n    private ExpressionAppendMode expressionAppendMode = ExpressionAppendMode.LAST;\n\n    protected void processSelectBody(Select selectBody, final String whereSegment) {\n        if (selectBody == null) {\n            return;\n        }\n        if (selectBody instanceof PlainSelect) {\n            processPlainSelect((PlainSelect) selectBody, whereSegment);\n        } else if (selectBody instanceof ParenthesedSelect) {\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processSelectBody(parenthesedSelect.getSelect(), whereSegment);\n        } else if (selectBody instanceof SetOperationList) {\n            SetOperationList operationList = (SetOperationList) selectBody;\n            List<Select> selectBodyList = operationList.getSelects();\n            if (CollectionUtils.isNotEmpty(selectBodyList)) {\n                selectBodyList.forEach(body -> processSelectBody(body, whereSegment));\n            }\n        }\n    }\n\n    /**\n     * delete update 语句 where 处理\n     */\n    protected Expression andExpression(Table table, Expression where, final String whereSegment) {\n        //获得where条件表达式\n        final Expression expression = buildTableExpression(table, where, whereSegment);\n        if (expression == null) {\n            return where;\n        }\n        if (where != null) {\n            if (where instanceof OrExpression) {\n                return appendExpression(new ParenthesedExpressionList<>(where), expression);\n            } else {\n                return appendExpression(where, expression);\n            }\n        }\n        return expression;\n    }\n\n    /**\n     * 处理 PlainSelect\n     */\n    protected void processPlainSelect(final PlainSelect plainSelect, final String whereSegment) {\n        //#3087 github\n        List<SelectItem<?>> selectItems = plainSelect.getSelectItems();\n        if (CollectionUtils.isNotEmpty(selectItems)) {\n            selectItems.forEach(selectItem -> processSelectItem(selectItem, whereSegment));\n        }\n\n        // 处理 where 中的子查询\n        Expression where = plainSelect.getWhere();\n        processWhereSubSelect(where, whereSegment);\n\n        // 处理 fromItem\n        FromItem fromItem = plainSelect.getFromItem();\n        List<Table> list = processFromItem(fromItem, whereSegment);\n        List<Table> mainTables = new ArrayList<>(list);\n\n        // 处理 join\n        List<Join> joins = plainSelect.getJoins();\n        if (CollectionUtils.isNotEmpty(joins)) {\n            processJoins(mainTables, joins, whereSegment);\n        }\n\n        // 当有 mainTable 时，进行 where 条件追加\n        if (CollectionUtils.isNotEmpty(mainTables)) {\n            plainSelect.setWhere(builderExpression(where, mainTables, whereSegment));\n        }\n    }\n\n    private List<Table> processFromItem(FromItem fromItem, final String whereSegment) {\n        // 处理括号括起来的表达式\n//        while (fromItem instanceof ParenthesedFromItem) {\n//            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n//        }\n\n        List<Table> mainTables = new ArrayList<>();\n        // 无 join 时的处理逻辑\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            mainTables.add(fromTable);\n        } else if (fromItem instanceof ParenthesedFromItem) {\n            // SubJoin 类型则还需要添加上 where 条件\n            List<Table> tables = processSubJoin((ParenthesedFromItem) fromItem, whereSegment);\n            mainTables.addAll(tables);\n        } else {\n            // 处理下 fromItem\n            processOtherFromItem(fromItem, whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理where条件内的子查询\n     * <p>\n     * 支持如下:\n     * <ol>\n     *     <li>in</li>\n     *     <li>=</li>\n     *     <li>&gt;</li>\n     *     <li>&lt;</li>\n     *     <li>&gt;=</li>\n     *     <li>&lt;=</li>\n     *     <li>&lt;&gt;</li>\n     *     <li>EXISTS</li>\n     *     <li>NOT EXISTS</li>\n     * </ol>\n     * <p>\n     * 前提条件:\n     * 1. 子查询必须放在小括号中\n     * 2. 子查询一般放在比较操作符的右边\n     *\n     * @param where where 条件\n     */\n    protected void processWhereSubSelect(Expression where, final String whereSegment) {\n        if (where == null) {\n            return;\n        }\n        if (where instanceof FromItem) {\n            processOtherFromItem((FromItem) where, whereSegment);\n            return;\n        }\n        if (where.toString().indexOf(\"SELECT\") > 0) {\n            // 有子查询\n            if (where instanceof BinaryExpression) {\n                // 比较符号 , and , or , 等等\n                BinaryExpression expression = (BinaryExpression) where;\n                processWhereSubSelect(expression.getLeftExpression(), whereSegment);\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof InExpression) {\n                // in\n                InExpression expression = (InExpression) where;\n                Expression inExpression = expression.getRightExpression();\n                if (inExpression instanceof Select) {\n                    processSelectBody(((Select) inExpression), whereSegment);\n                }\n            } else if (where instanceof ExistsExpression) {\n                // exists\n                ExistsExpression expression = (ExistsExpression) where;\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof NotExpression) {\n                // not exists\n                NotExpression expression = (NotExpression) where;\n                processWhereSubSelect(expression.getExpression(), whereSegment);\n            } else if (where instanceof ParenthesedExpressionList) {\n                ParenthesedExpressionList<Expression> expression = (ParenthesedExpressionList) where;\n                processWhereSubSelect(expression.get(0), whereSegment);\n            }\n        }\n    }\n\n    protected void processSelectItem(SelectItem selectItem, final String whereSegment) {\n        Expression expression = selectItem.getExpression();\n        if (expression instanceof Select) {\n            processSelectBody(((Select) expression), whereSegment);\n        } else if (expression instanceof Function) {\n            processFunction((Function) expression, whereSegment);\n        } else if (expression instanceof ExistsExpression) {\n            ExistsExpression existsExpression = (ExistsExpression) expression;\n            processSelectBody((Select) existsExpression.getRightExpression(), whereSegment);\n        }\n    }\n\n    /**\n     * 处理函数\n     * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>\n     * <p> fixed gitee pulls/141</p>\n     *\n     * @param function\n     */\n    protected void processFunction(Function function, final String whereSegment) {\n        ExpressionList<?> parameters = function.getParameters();\n        if (parameters != null) {\n            parameters.forEach(expression -> {\n                if (expression instanceof Select) {\n                    processSelectBody(((Select) expression), whereSegment);\n                } else if (expression instanceof Function) {\n                    processFunction((Function) expression, whereSegment);\n                } else if (expression instanceof EqualsTo) {\n                    if (((EqualsTo) expression).getLeftExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getLeftExpression()), whereSegment);\n                    }\n                    if (((EqualsTo) expression).getRightExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getRightExpression()), whereSegment);\n                    }\n                }\n            });\n        }\n    }\n\n    /**\n     * 处理子查询等\n     */\n    protected void processOtherFromItem(FromItem fromItem, final String whereSegment) {\n        // 去除括号\n        while (fromItem instanceof ParenthesedFromItem) {\n            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n        }\n        if (fromItem instanceof ParenthesedSelect) {\n            Select subSelect = (Select) fromItem;\n            processSelectBody(subSelect, whereSegment);\n        }\n    }\n\n    /**\n     * 处理 sub join\n     *\n     * @param subJoin subJoin\n     * @return Table subJoin 中的主表\n     */\n    private List<Table> processSubJoin(ParenthesedFromItem subJoin, final String whereSegment) {\n        while (subJoin.getJoins() == null && subJoin.getFromItem() instanceof ParenthesedFromItem) {\n            subJoin = (ParenthesedFromItem) subJoin.getFromItem();\n        }\n        List<Table> tableList = processFromItem(subJoin.getFromItem(), whereSegment);\n        List<Table> mainTables = new ArrayList<>(tableList);\n        if (subJoin.getJoins() != null) {\n            processJoins(mainTables, subJoin.getJoins(), whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理 joins\n     *\n     * @param mainTables 可以为 null\n     * @param joins      join 集合\n     * @return List<Table> 右连接查询的 Table 列表\n     */\n    private List<Table> processJoins(List<Table> mainTables, List<Join> joins, final String whereSegment) {\n        // join 表达式中最终的主表\n        Table mainTable = null;\n        // 当前 join 的左表\n        Table leftTable = null;\n\n        if (mainTables.size() == 1) {\n            mainTable = mainTables.get(0);\n            leftTable = mainTable;\n        }\n\n        //对于 on 表达式写在最后的 join，需要记录下前面多个 on 的表名\n        Deque<List<Table>> onTableDeque = new LinkedList<>();\n        for (Join join : joins) {\n            // 处理 on 表达式\n            FromItem joinItem = join.getRightItem();\n\n            // 获取当前 join 的表，subJoint 可以看作是一张表\n            List<Table> joinTables = null;\n            if (joinItem instanceof Table) {\n                joinTables = new ArrayList<>();\n                joinTables.add((Table) joinItem);\n            } else if (joinItem instanceof ParenthesedFromItem) {\n                joinTables = processSubJoin((ParenthesedFromItem) joinItem, whereSegment);\n            }\n\n            if (joinTables != null && !joinTables.isEmpty()) {\n\n                // 如果是隐式内连接\n                if (join.isSimple()) {\n                    mainTables.addAll(joinTables);\n                    continue;\n                }\n\n                // 当前表是否忽略\n                Table joinTable = joinTables.get(0);\n\n                List<Table> onTables = null;\n                // 如果不要忽略，且是右连接，则记录下当前表\n                if (join.isRight()) {\n                    mainTable = joinTable;\n                    mainTables.clear();\n                    if (leftTable != null) {\n                        onTables = Collections.singletonList(leftTable);\n                    }\n                } else if (join.isInner()) {\n                    if (mainTable == null) {\n                        onTables = Collections.singletonList(joinTable);\n                    } else {\n                        onTables = Arrays.asList(mainTable, joinTable);\n                    }\n                    mainTable = null;\n                    mainTables.clear();\n                } else {\n                    onTables = Collections.singletonList(joinTable);\n                }\n\n                if (mainTable != null && !mainTables.contains(mainTable)) {\n                    mainTables.add(mainTable);\n                }\n\n                // 获取 join 尾缀的 on 表达式列表\n                Collection<Expression> originOnExpressions = join.getOnExpressions();\n                // 正常 join on 表达式只有一个，立刻处理\n                if (originOnExpressions.size() == 1 && onTables != null) {\n                    List<Expression> onExpressions = new LinkedList<>();\n                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables, whereSegment));\n                    join.setOnExpressions(onExpressions);\n                    leftTable = mainTable == null ? joinTable : mainTable;\n                    continue;\n                }\n                // 表名压栈，忽略的表压入 null，以便后续不处理\n                onTableDeque.push(onTables);\n                // 尾缀多个 on 表达式的时候统一处理\n                if (originOnExpressions.size() > 1) {\n                    Collection<Expression> onExpressions = new LinkedList<>();\n                    for (Expression originOnExpression : originOnExpressions) {\n                        List<Table> currentTableList = onTableDeque.poll();\n                        if (CollectionUtils.isEmpty(currentTableList)) {\n                            onExpressions.add(originOnExpression);\n                        } else {\n                            onExpressions.add(builderExpression(originOnExpression, currentTableList, whereSegment));\n                        }\n                    }\n                    join.setOnExpressions(onExpressions);\n                }\n                leftTable = joinTable;\n            } else {\n                processOtherFromItem(joinItem, whereSegment);\n                leftTable = null;\n            }\n        }\n\n        return mainTables;\n    }\n\n    /**\n     * 处理条件\n     */\n    protected Expression builderExpression(Expression currentExpression, List<Table> tables, final String whereSegment) {\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(tables)) {\n            return currentExpression;\n        }\n        // 构造每张表的条件\n        List<Expression> expressions = tables.stream()\n                .map(item -> buildTableExpression(item, currentExpression, whereSegment))\n                .filter(Objects::nonNull)\n                .collect(Collectors.toList());\n\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(expressions)) {\n            return currentExpression;\n        }\n\n        // 注入的表达式\n        Expression injectExpression = expressions.get(0);\n        // 如果有多表，则用 and 连接\n        if (expressions.size() > 1) {\n            for (int i = 1; i < expressions.size(); i++) {\n                injectExpression = new AndExpression(injectExpression, expressions.get(i));\n            }\n        }\n\n        if (currentExpression == null) {\n            return injectExpression;\n        }\n        if (currentExpression instanceof OrExpression) {\n            return appendExpression(new ParenthesedExpressionList<>(currentExpression), injectExpression);\n        } else {\n            return appendExpression(currentExpression, injectExpression);\n        }\n    }\n\n    /**\n     * 追加表达式，默认追加到后面，可以配置变量 {@link #expressionAppendMode} 来控制追加到前面还是后面\n     *\n     * @param currentExpression 原sql的条件表达式\n     * @param injectExpression  注入的表达式\n     * @return 追加了条件的完整表达式(where条件 / on条件)\n     * @since 3.5.11\n     */\n    protected Expression appendExpression(Expression currentExpression, Expression injectExpression) {\n        if (ExpressionAppendMode.LAST == expressionAppendMode || expressionAppendMode == null) {\n            return new AndExpression(currentExpression, injectExpression);\n        } else {\n            return new AndExpression(injectExpression, currentExpression);\n        }\n    }\n\n    /**\n     * 构建数据库表的查询条件\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径\n     * @return 需要拼接的新条件（不会覆盖原有的where条件，只会在原有条件上再加条件），为 null 则不加入新的条件\n     */\n    public abstract Expression buildTableExpression(final Table table, final Expression where, final String whereSegment);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BlockAttackInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.IsNullExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\n\n/**\n * 攻击 SQL 阻断解析器,防止全表更新与删除\n *\n * @author hubin\n * @since 3.4.0\n */\npublic class BlockAttackInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler handler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = handler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreBlockAttack(ms.getId())) {\n                return;\n            }\n            BoundSql boundSql = handler.boundSql();\n            parserMulti(boundSql.getSql(), null);\n        }\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        this.checkWhere(delete.getTable().getName(), delete.getWhere(), \"Prohibition of full table deletion\");\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        this.checkWhere(update.getTable().getName(), update.getWhere(), \"Prohibition of table update operation\");\n    }\n\n    protected void checkWhere(String tableName, Expression where, String ex) {\n        Assert.isFalse(this.fullMatch(where, this.getTableLogicField(tableName)), ex);\n    }\n\n    private boolean fullMatch(Expression where, String logicField) {\n        if (where == null) {\n            return true;\n        }\n        if (StringUtils.isNotBlank(logicField)) {\n\n            if (where instanceof BinaryExpression) {\n                BinaryExpression binaryExpression = (BinaryExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField) || StringUtils.equals(binaryExpression.getRightExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n\n            if (where instanceof IsNullExpression) {\n                IsNullExpression binaryExpression = (IsNullExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n        }\n\n        if (where instanceof EqualsTo) {\n            // example: 1=1\n            EqualsTo equalsTo = (EqualsTo) where;\n            return StringUtils.equals(equalsTo.getLeftExpression().toString(), equalsTo.getRightExpression().toString());\n        } else if (where instanceof NotEqualsTo) {\n            // example: 1 != 2\n            NotEqualsTo notEqualsTo = (NotEqualsTo) where;\n            return !StringUtils.equals(notEqualsTo.getLeftExpression().toString(), notEqualsTo.getRightExpression().toString());\n        } else if (where instanceof OrExpression) {\n\n            OrExpression orExpression = (OrExpression) where;\n            return fullMatch(orExpression.getLeftExpression(), logicField) || fullMatch(orExpression.getRightExpression(), logicField);\n        } else if (where instanceof AndExpression) {\n\n            AndExpression andExpression = (AndExpression) where;\n            return fullMatch(andExpression.getLeftExpression(), logicField) && fullMatch(andExpression.getRightExpression(), logicField);\n        } else if (where instanceof ParenthesedExpressionList) {\n            // example: (1 = 1)\n            ParenthesedExpressionList<Expression> parenthesis = (ParenthesedExpressionList<Expression>) where;\n            return fullMatch(parenthesis.get(0), logicField);\n        }\n\n        return false;\n    }\n\n    /**\n     * 获取表名中的逻辑删除字段\n     *\n     * @param tableName 表名\n     * @return 逻辑删除字段\n     */\n    private String getTableLogicField(String tableName) {\n        if (StringUtils.isBlank(tableName)) {\n            return StringPool.EMPTY;\n        }\n\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);\n        if (tableInfo == null || !tableInfo.isWithLogicDelete() || tableInfo.getLogicDeleteFieldInfo() == null) {\n            return StringPool.EMPTY;\n        }\n        return tableInfo.getLogicDeleteFieldInfo().getColumn();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataChangeRecorderInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.io.Reader;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport net.sf.jsqlparser.statement.select.Values;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.SystemMetaObject;\nimport org.apache.ibatis.scripting.defaults.DefaultParameterHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.baomidou.mybatisplus.annotation.IEnum;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\n\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.JdbcParameter;\nimport net.sf.jsqlparser.expression.RowConstructor;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.AllColumns;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SelectItem;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\n\n/**\n * <p>\n * 数据变动记录插件\n * 默认会生成一条log，格式：\n * ----------------------INSERT LOG------------------------------\n * </p>\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"insert\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\",\n * \"TEST_ID\": \"null->1561666810058739714\",\n * \"AGE\": \"null->THREE\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n * <p>\n * * ----------------------UPDATE LOG------------------------------\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"update\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"TEST_ID\": \"102\",\n * \"AGE\": \"2->THREE\",\n * \"FIRSTNAME\": \"DOU.HAO->{\\\"json\\\":\\\"abc\\\"}\",\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n *\n * @author yuxiaobin\n * @deprecated 3.5.10 问题太多,计划移除\n * @date 2022-8-21\n */\n@Deprecated\npublic class DataChangeRecorderInnerInterceptor implements InnerInterceptor {\n\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @SuppressWarnings(\"unused\")\n    public static final String IGNORED_TABLE_COLUMN_PROPERTIES = \"ignoredTableColumns\";\n\n    private final Map<String, Set<String>> ignoredTableColumns = new ConcurrentHashMap<>();\n    private final Set<String> ignoreAllColumns = new HashSet<>();//全部表的这些字段名，INSERT/UPDATE都忽略，delete暂时保留\n    //批量更新上限, 默认一次最多1000条\n    private int BATCH_UPDATE_LIMIT = 1000;\n    private boolean batchUpdateLimitationOpened = false;\n    private final Map<String, Integer> BATCH_UPDATE_LIMIT_MAP = new ConcurrentHashMap<>();//表名->批量更新上限\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        final BoundSql boundSql = mpSh.boundSql();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            OperationResult operationResult;\n            long startTs = System.currentTimeMillis();\n            try {\n                Statement statement = JsqlParserGlobal.parse(mpBs.sql());\n                if (statement instanceof Insert) {\n                    operationResult = processInsert((Insert) statement, mpSh.boundSql());\n                } else if (statement instanceof Update) {\n                    operationResult = processUpdate((Update) statement, ms, boundSql, connection);\n                } else if (statement instanceof Delete) {\n                    operationResult = processDelete((Delete) statement, ms, boundSql, connection);\n                } else {\n                    logger.info(\"other operation sql={}\", mpBs.sql());\n                    return;\n                }\n            } catch (Exception e) {\n                if (e instanceof DataUpdateLimitationException) {\n                    throw (DataUpdateLimitationException) e;\n                }\n                logger.error(\"Unexpected error for mappedStatement={}, sql={}\", ms.getId(), mpBs.sql(), e);\n                return;\n            }\n            long costThis = System.currentTimeMillis() - startTs;\n            if (operationResult != null) {\n                operationResult.setCost(costThis);\n                dealOperationResult(operationResult);\n            }\n        }\n    }\n\n    /**\n     * 判断哪些SQL需要处理\n     * 默认INSERT/UPDATE/DELETE语句\n     *\n     * @param sql\n     * @return\n     */\n    protected boolean allowProcess(String sql) {\n        String sqlTrim = sql.trim().toUpperCase();\n        return sqlTrim.startsWith(\"INSERT\") || sqlTrim.startsWith(\"UPDATE\") || sqlTrim.startsWith(\"DELETE\");\n    }\n\n    /**\n     * 处理数据更新结果，默认打印\n     *\n     * @param operationResult\n     */\n    protected void dealOperationResult(OperationResult operationResult) {\n        logger.info(\"{}\", operationResult);\n    }\n\n    public OperationResult processInsert(Insert insertStmt, BoundSql boundSql) {\n        String operation = SqlCommandType.INSERT.name().toLowerCase();\n        Table table = insertStmt.getTable();\n        String tableName = table.getName();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, insertStmt);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), null, updatedColumnDatas));\n        return result;\n    }\n\n    public OperationResult processUpdate(Update updateStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Expression where = updateStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        Table table = updateStmt.getTable();\n        String tableName = table.getName();\n        String operation = SqlCommandType.UPDATE.name().toLowerCase();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        selectBody.setFromItem(table);\n        List<Column> updateColumns = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateColumns.addAll(updateSet.getColumns());\n        }\n        Columns2SelectItemsResult buildColumns2SelectItems = buildColumns2SelectItems(tableName, updateColumns);\n        selectBody.setSelectItems(buildColumns2SelectItems.getSelectItems());\n        selectBody.setWhere(where);\n        SelectItem<PlainSelect> plainSelectSelectItem = new SelectItem<>(selectBody);\n\n        BoundSql boundSql4Select = new BoundSql(mappedStatement.getConfiguration(), plainSelectSelectItem.toString(),\n            prepareParameterMapping4Select(boundSql.getParameterMappings(), updateStmt),\n            boundSql.getParameterObject());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n        Map<String, Object> additionalParameters = mpBoundSql.additionalParameters();\n        if (additionalParameters != null && !additionalParameters.isEmpty()) {\n            for (Map.Entry<String, Object> ety : additionalParameters.entrySet()) {\n                boundSql4Select.setAdditionalParameter(ety.getKey(), ety.getValue());\n            }\n        }\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, updateStmt);\n        OriginalDataObj originalData = buildOriginalObjectData(updatedColumnDatas, selectBody, buildColumns2SelectItems.getPk(), mappedStatement, boundSql4Select, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), originalData, updatedColumnDatas));\n        return result;\n    }\n\n    private Optional<OperationResult> ignoredTableColumns(String table, String operation) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(table.toUpperCase());\n        if (ignoredColumns != null) {\n            if (ignoredColumns.stream().anyMatch(\"*\"::equals)) {\n                OperationResult result = new OperationResult();\n                result.setOperation(operation);\n                result.setTableName(table + \":*\");\n                result.setRecordStatus(false);\n                return Optional.of(result);\n            }\n        }\n        return Optional.empty();\n    }\n\n    private TableInfo getTableInfoByTableName(String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                return tableInfo;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 将update SET部分的jdbc参数去除\n     *\n     * @param originalMappingList 这里只会包含JdbcParameter参数\n     * @param updateStmt\n     * @return\n     */\n    private List<ParameterMapping> prepareParameterMapping4Select(List<ParameterMapping> originalMappingList, Update updateStmt) {\n        List<Expression> updateValueExpressions = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateValueExpressions.addAll(updateSet.getValues());\n        }\n        int removeParamCount = 0;\n        for (Expression expression : updateValueExpressions) {\n            if (expression instanceof JdbcParameter) {\n                ++removeParamCount;\n            }\n        }\n        return originalMappingList.subList(removeParamCount, originalMappingList.size());\n    }\n\n    protected Map<String, Object> getUpdatedColumnDatas(String tableName, BoundSql updateSql, Statement statement) {\n        Map<String, Object> columnNameValMap = new HashMap<>(updateSql.getParameterMappings().size());\n        Map<Integer, String> columnSetIndexMap = new HashMap<>(updateSql.getParameterMappings().size());\n        List<Column> selectItemsFromUpdateSql = new ArrayList<>();\n        if (statement instanceof Update) {\n            Update updateStmt = (Update) statement;\n            int index = 0;\n            for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n                selectItemsFromUpdateSql.addAll(updateSet.getColumns());\n                final ExpressionList<Expression> updateList = (ExpressionList<Expression>) updateSet.getValues();\n                for (int i = 0; i < updateList.size(); ++i) {\n                    Expression updateExps = updateList.get(i);\n                    if (!(updateExps instanceof JdbcParameter)) {\n                        columnNameValMap.put(updateSet.getColumns().get(i).getColumnName().toUpperCase(), updateExps.toString());\n                    }\n                    columnSetIndexMap.put(index++, updateSet.getColumns().get(i).getColumnName().toUpperCase());\n                }\n            }\n        } else if (statement instanceof Insert) {\n            Insert insert = (Insert) statement;\n            selectItemsFromUpdateSql.addAll(insert.getColumns());\n            columnNameValMap.putAll(detectInsertColumnValuesNonJdbcParameters(insert));\n        }\n        Map<String, String> relatedColumnsUpperCaseWithoutUnderline = new HashMap<>(selectItemsFromUpdateSql.size(), 1);\n        for (Column item : selectItemsFromUpdateSql) {\n            //FIRSTNAME: FIRST_NAME/FIRST-NAME/FIRST$NAME/FIRST.NAME\n            relatedColumnsUpperCaseWithoutUnderline.put(item.getColumnName().replaceAll(\"[._\\\\-$]\", \"\").toUpperCase(), item.getColumnName().toUpperCase());\n        }\n        MetaObject metaObject = SystemMetaObject.forObject(updateSql.getParameterObject());\n        int index = 0;\n        for (ParameterMapping parameterMapping : updateSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                ++index;\n                continue;\n            }\n            String[] arr = propertyName.split(\"\\\\.\");\n            String propertyNameTrim = arr[arr.length - 1].replace(\"_\", \"\").toUpperCase();\n            try {\n                final String columnName = columnSetIndexMap.getOrDefault(index++, getColumnNameByProperty(propertyNameTrim, tableName));\n                if (relatedColumnsUpperCaseWithoutUnderline.containsKey(propertyNameTrim)) {\n                    final String colkey = relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim);\n                    Object valObj = metaObject.getValue(propertyName);\n                    if (valObj instanceof IEnum) {\n                        valObj = ((IEnum<?>) valObj).getValue();\n                    } else if (valObj instanceof Enum) {\n                        valObj = getEnumValue((Enum) valObj);\n                    }\n                    if (columnNameValMap.containsKey(colkey)) {\n                        columnNameValMap.put(relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim), String.valueOf(columnNameValMap.get(colkey)).replace(\"?\", valObj == null ? \"\" : valObj.toString()));\n                    }\n                    if (columnName != null && !columnNameValMap.containsKey(columnName)) {\n                        columnNameValMap.put(columnName, valObj);\n                    }\n                } else {\n                    if (columnName != null) {\n                        columnNameValMap.put(columnName, metaObject.getValue(propertyName));\n                    }\n                }\n            } catch (Exception e) {\n                logger.warn(\"get value error,propertyName:{},parameterMapping:{}\", propertyName, parameterMapping);\n            }\n        }\n        dealWithUpdateWrapper(columnSetIndexMap, columnNameValMap, updateSql);\n        return columnNameValMap;\n    }\n\n    /**\n     * @param originalDataObj\n     * @return\n     */\n    private List<DataChangedRecord> compareAndGetUpdatedColumnDatas(String tableName, OriginalDataObj originalDataObj, Map<String, Object> columnNameValMap) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(tableName.toUpperCase());\n        if (originalDataObj == null || originalDataObj.isEmpty()) {\n            DataChangedRecord oneRecord = new DataChangedRecord();\n            List<DataColumnChangeResult> updateColumns = new ArrayList<>(columnNameValMap.size());\n            for (Map.Entry<String, Object> ety : columnNameValMap.entrySet()) {\n                String columnName = ety.getKey();\n                if ((ignoredColumns == null || !ignoredColumns.contains(columnName)) && !ignoreAllColumns.contains(columnName)) {\n                    updateColumns.add(DataColumnChangeResult.constrcutByUpdateVal(columnName, ety.getValue()));\n                }\n            }\n            oneRecord.setUpdatedColumns(updateColumns);\n//            oneRecord.setUpdatedColumns(Collections.EMPTY_LIST);\n            return Collections.singletonList(oneRecord);\n        }\n        List<DataChangedRecord> originalDataList = originalDataObj.getOriginalDataObj();\n        List<DataChangedRecord> updateDataList = new ArrayList<>(originalDataList.size());\n        for (DataChangedRecord originalData : originalDataList) {\n            if (originalData.hasUpdate(columnNameValMap, ignoredColumns, ignoreAllColumns)) {\n                updateDataList.add(originalData);\n            }\n        }\n        return updateDataList;\n    }\n\n    private Object getEnumValue(Enum enumVal) {\n        Optional<String> enumValueFieldName = MybatisEnumTypeHandler.findEnumValueFieldName(enumVal.getClass());\n        if (enumValueFieldName.isPresent()) {\n            return SystemMetaObject.forObject(enumVal).getValue(enumValueFieldName.get());\n        }\n        return enumVal;\n\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    private void dealWithUpdateWrapper(Map<Integer, String> columnSetIndexMap, Map<String, Object> columnNameValMap, BoundSql updateSql) {\n        if (columnSetIndexMap.size() <= columnNameValMap.size()) {\n            return;\n        }\n        MetaObject mpgenVal = SystemMetaObject.forObject(updateSql.getParameterObject());\n        if(!mpgenVal.hasGetter(Constants.WRAPPER)){\n            return;\n        }\n        Object ew = mpgenVal.getValue(Constants.WRAPPER);\n        if (ew instanceof UpdateWrapper || ew instanceof LambdaUpdateWrapper) {\n            final String sqlSet = ew instanceof UpdateWrapper ? ((UpdateWrapper) ew).getSqlSet() : ((LambdaUpdateWrapper) ew).getSqlSet();// columnName=#{val}\n            if (sqlSet == null) {\n                return;\n            }\n            MetaObject ewMeta = SystemMetaObject.forObject(ew);\n            final Map paramNameValuePairs = (Map) ewMeta.getValue(\"paramNameValuePairs\");\n            String[] setItems = sqlSet.split(\",\");\n            for (String setItem : setItems) {\n                //age=#{ew.paramNameValuePairs.MPGENVAL1}\n                String[] nameAndValuePair = setItem.split(\"=\", 2);\n                if (nameAndValuePair.length == 2) {\n                    String setColName = nameAndValuePair[0].trim().toUpperCase();\n                    String setColVal = nameAndValuePair[1].trim();//#{.mp}\n                    if (columnSetIndexMap.containsValue(setColName)) {\n                        String[] mpGenKeyArray = setColVal.split(\"\\\\.\");\n                        String mpGenKey = mpGenKeyArray[mpGenKeyArray.length - 1].replace(\"}\", \"\");\n                        final Object setVal = paramNameValuePairs.get(mpGenKey);\n                        if (setVal instanceof IEnum) {\n                            columnNameValMap.put(setColName, String.valueOf(((IEnum<?>) setVal).getValue()));\n                        } else {\n                            columnNameValMap.put(setColName, setVal);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private Map<String, String> detectInsertColumnValuesNonJdbcParameters(Insert insert) {\n        Map<String, String> columnNameValMap = new HashMap<>(4);\n        final Select select = insert.getSelect();\n        List<Column> columns = insert.getColumns();\n        if (select instanceof SetOperationList) {\n            SetOperationList setOperationList = (SetOperationList) select;\n            final List<Select> selects = setOperationList.getSelects();\n            if (CollectionUtils.isEmpty(selects)) {\n                return columnNameValMap;\n            }\n            final Select selectBody = selects.get(0);\n            if (!(selectBody instanceof Values)) {\n                return columnNameValMap;\n            }\n            Values valuesStatement = (Values) selectBody;\n            if (valuesStatement.getExpressions() instanceof ExpressionList) {\n                ExpressionList expressionList = valuesStatement.getExpressions();\n                List<Expression> expressions = expressionList;\n                for (Expression expression : expressions) {\n                    if (expression instanceof RowConstructor) {\n                        final ExpressionList exprList = ((RowConstructor) expression);\n                        final List<Expression> insertExpList = exprList;\n                        for (int i = 0; i < insertExpList.size(); ++i) {\n                            Expression e = insertExpList.get(i);\n                            if (!(e instanceof JdbcParameter)) {\n                                final String columnName = columns.get(i).getColumnName();\n                                final String val = e.toString();\n                                columnNameValMap.put(columnName, val);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return columnNameValMap;\n    }\n\n    private String getColumnNameByProperty(String propertyName, String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                final List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n                if (CollectionUtils.isEmpty(fieldList)) {\n                    return propertyName;\n                }\n                for (TableFieldInfo tableFieldInfo : fieldList) {\n                    if (propertyName.equalsIgnoreCase(tableFieldInfo.getProperty())) {\n                        return tableFieldInfo.getColumn().toUpperCase();\n                    }\n                }\n                return propertyName;\n            }\n        }\n        return propertyName;\n    }\n\n\n    private Map<String, Object> buildParameterObjectMap(BoundSql boundSql) {\n        MetaObject metaObject = PluginUtils.getMetaObject(boundSql.getParameterObject());\n        Map<String, Object> propertyValMap = new HashMap<>(boundSql.getParameterMappings().size());\n        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                continue;\n            }\n            Object propertyValue = metaObject.getValue(propertyName);\n            propertyValMap.put(propertyName, propertyValue);\n        }\n        return propertyValMap;\n\n    }\n\n\n    private String buildOriginalData(Select selectStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            final ResultSetMetaData metaData = resultSet.getMetaData();\n            int columnCount = metaData.getColumnCount();\n            StringBuilder sb = new StringBuilder(\"[\");\n            int count = 0;\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch delete limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                sb.append(\"{\");\n                for (int i = 1; i <= columnCount; ++i) {\n                    sb.append(\"\\\"\").append(metaData.getColumnName(i)).append(\"\\\":\\\"\");\n                    Object res = resultSet.getObject(i);\n                    if (res instanceof Clob) {\n                        sb.append(DataColumnChangeResult.convertClob((Clob) res));\n                    } else {\n                        sb.append(res);\n                    }\n                    sb.append(\"\\\",\");\n                }\n                sb.replace(sb.length() - 1, sb.length(), \"}\");\n            }\n            sb.append(\"]\");\n            resultSet.close();\n            return sb.toString();\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe deleted for selectStmt={}\", selectStmt, e);\n            return \"failed to get original data\";\n        }\n    }\n\n    private OriginalDataObj buildOriginalObjectData(Map<String, Object> updatedColumnDatas, Select selectStmt, Column pk, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            List<DataChangedRecord> originalObjectDatas = new LinkedList<>();\n            int count = 0;\n\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch update limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                originalObjectDatas.add(prepareOriginalDataObj(updatedColumnDatas, resultSet, pk));\n            }\n            OriginalDataObj result = new OriginalDataObj();\n            result.setOriginalDataObj(originalObjectDatas);\n            resultSet.close();\n            return result;\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe updated for selectStmt={}\", selectStmt, e);\n            return new OriginalDataObj();\n        }\n    }\n\n    /**\n     * 防止出现全表批量更新\n     * 默认一次更新不超过1000条\n     *\n     * @param selectStmt\n     * @param count\n     * @return\n     */\n    private boolean checkTableBatchLimitExceeded(Select selectStmt, int count) {\n        if (!batchUpdateLimitationOpened) {\n            return false;\n        }\n        final PlainSelect selectBody = (PlainSelect) selectStmt;\n        final FromItem fromItem = selectBody.getFromItem();\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            final String tableName = fromTable.getName().toUpperCase();\n            if (!BATCH_UPDATE_LIMIT_MAP.containsKey(tableName)) {\n                if (count > BATCH_UPDATE_LIMIT) {\n                    logger.error(\"batch update limit exceed for tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                        tableName, BATCH_UPDATE_LIMIT, count);\n                    return true;\n                }\n                return false;\n            }\n            final Integer limit = BATCH_UPDATE_LIMIT_MAP.get(tableName);\n            if (count > limit) {\n                logger.error(\"batch update limit exceed for configured tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                    tableName, limit, count);\n                return true;\n            }\n            return false;\n        }\n        return count > BATCH_UPDATE_LIMIT;\n    }\n\n\n    /**\n     * get records : include related column with original data in DB\n     *\n     * @param resultSet\n     * @param pk\n     * @return\n     * @throws SQLException\n     */\n    private DataChangedRecord prepareOriginalDataObj(Map<String, Object> updatedColumnDatas, ResultSet resultSet, Column pk) throws SQLException {\n        final ResultSetMetaData metaData = resultSet.getMetaData();\n        int columnCount = metaData.getColumnCount();\n        List<DataColumnChangeResult> originalColumnDatas = new LinkedList<>();\n        DataColumnChangeResult pkval = null;\n        for (int i = 1; i <= columnCount; ++i) {\n            String columnName = metaData.getColumnName(i).toUpperCase();\n            DataColumnChangeResult col;\n            Object updateVal = updatedColumnDatas.get(columnName);\n            if (updateVal != null && updateVal.getClass().getCanonicalName().startsWith(\"java.\")) {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i, updateVal.getClass()));\n            } else {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i));\n            }\n            if (pk != null && columnName.equalsIgnoreCase(pk.getColumnName())) {\n                pkval = col;\n            } else {\n                originalColumnDatas.add(col);\n            }\n        }\n        DataChangedRecord changedRecord = new DataChangedRecord();\n        changedRecord.setOriginalColumnDatas(originalColumnDatas);\n        if (pkval != null) {\n            changedRecord.setPkColumnName(pkval.getColumnName());\n            changedRecord.setPkColumnVal(pkval.getOriginalValue());\n        }\n        return changedRecord;\n    }\n\n\n    private Columns2SelectItemsResult buildColumns2SelectItems(String tableName, List<Column> columns) {\n        if (columns == null || columns.isEmpty()) {\n            return Columns2SelectItemsResult.build(Collections.singletonList(new SelectItem<>(new AllColumns())), 0);\n        }\n        List<SelectItem<?>> selectItems = new ArrayList<>(columns.size());\n        for (Column column : columns) {\n            selectItems.add(new SelectItem<>(column));\n        }\n        TableInfo tableInfo = getTableInfoByTableName(tableName);\n        if (tableInfo == null || StringUtils.isBlank(tableInfo.getKeyColumn())) {\n            return Columns2SelectItemsResult.build(selectItems, 0);\n        }\n        Column pk = new Column(tableInfo.getKeyColumn());\n        selectItems.add(new SelectItem<>(pk));\n        Columns2SelectItemsResult result = Columns2SelectItemsResult.build(selectItems, 1);\n        result.setPk(pk);\n        return result;\n    }\n\n    private String buildParameterObject(BoundSql boundSql) {\n        Object paramObj = boundSql.getParameterObject();\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"{\");\n        if (paramObj instanceof Map) {\n            Map<String, Object> paramMap = (Map<String, Object>) paramObj;\n            int index = 1;\n            boolean hasParamIndex = false;\n            String key;\n            while (paramMap.containsKey((key = \"param\" + index))) {\n                Object paramIndex = paramMap.get(key);\n                sb.append(\"\\\"\").append(key).append(\"\\\"\").append(\":\").append(\"\\\"\").append(paramIndex).append(\"\\\"\").append(\",\");\n                hasParamIndex = true;\n                ++index;\n            }\n            if (hasParamIndex) {\n                sb.delete(sb.length() - 1, sb.length());\n                sb.append(\"}\");\n                return sb.toString();\n            }\n            for (Map.Entry<String, Object> ety : paramMap.entrySet()) {\n                sb.append(\"\\\"\").append(ety.getKey()).append(\"\\\"\").append(\":\").append(\"\\\"\").append(ety.getValue()).append(\"\\\"\").append(\",\");\n            }\n            sb.delete(sb.length() - 1, sb.length());\n            sb.append(\"}\");\n            return sb.toString();\n        }\n        sb.append(\"param:\").append(paramObj);\n        sb.append(\"}\");\n        return sb.toString();\n    }\n\n    public OperationResult processDelete(Delete deleteStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Table table = deleteStmt.getTable();\n        Expression where = deleteStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        selectBody.setFromItem(table);\n        selectBody.setSelectItems(Collections.singletonList(new SelectItem<>((new AllColumns()))));\n        selectBody.setWhere(where);\n        String originalData = buildOriginalData(selectBody, mappedStatement, boundSql, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(\"delete\");\n        result.setTableName(table.getName());\n        result.setRecordStatus(originalData.startsWith(\"[\"));\n        result.setChangedData(originalData);\n        return result;\n    }\n\n    /**\n     * 设置批量更新记录条数上限\n     *\n     * @param limit\n     * @return\n     */\n    public DataChangeRecorderInnerInterceptor setBatchUpdateLimit(int limit) {\n        this.BATCH_UPDATE_LIMIT = limit;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor openBatchUpdateLimitation() {\n        this.batchUpdateLimitationOpened = true;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor configTableLimitation(String tableName, int limit) {\n        this.BATCH_UPDATE_LIMIT_MAP.put(tableName.toUpperCase(), limit);\n        return this;\n    }\n\n    /**\n     * ignoredColumns = TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2; TABLE3.*; *.COLUMN1,COLUMN2\n     * 多个表用分号分隔\n     * TABLE_NAME1.COLUMN1,COLUMN2 : 表示忽略这个表的这2个字段\n     * TABLE3.*: 表示忽略这张表的INSERT/UPDATE，delete暂时还保留\n     * *.COLUMN1,COLUMN2:表示所有表的这个2个字段名都忽略\n     *\n     * @param properties\n     */\n    @Override\n    public void setProperties(Properties properties) {\n\n        String ignoredTableColumns = properties.getProperty(\"ignoredTableColumns\");\n        if (ignoredTableColumns == null || ignoredTableColumns.trim().isEmpty()) {\n            return;\n        }\n        String[] array = ignoredTableColumns.split(\";\");\n        for (String table : array) {\n            int index = table.indexOf(\".\");\n            if (index == -1) {\n                logger.warn(\"invalid data={} for ignoredColumns, format should be TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2;\", table);\n                continue;\n            }\n            String tableName = table.substring(0, index).trim().toUpperCase();\n            String[] columnArray = table.substring(index + 1).split(\",\");\n            Set<String> columnSet = new HashSet<>(columnArray.length);\n            for (String column : columnArray) {\n                column = column.trim().toUpperCase();\n                if (column.isEmpty()) {\n                    continue;\n                }\n                columnSet.add(column);\n            }\n            if (\"*\".equals(tableName)) {\n                ignoreAllColumns.addAll(columnSet);\n            } else {\n                this.ignoredTableColumns.put(tableName, columnSet);\n            }\n        }\n    }\n\n    @Data\n    public static class OperationResult {\n\n        private String operation;\n        private boolean recordStatus;\n        private String tableName;\n        private String changedData;\n        /**\n         * cost for this plugin, ms\n         */\n        private long cost;\n\n        public void buildDataStr(List<DataChangedRecord> records) {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"[\");\n            for (DataChangedRecord r : records) {\n                sb.append(r.generateUpdatedDataStr()).append(\",\");\n            }\n            if (sb.length() == 1) {\n                sb.append(\"]\");\n                changedData = sb.toString();\n                return;\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"]\");\n            changedData = sb.toString();\n        }\n\n        @Override\n        public String toString() {\n            return \"{\" +\n                \"\\\"tableName\\\":\\\"\" + tableName + \"\\\",\" +\n                \"\\\"operation\\\":\\\"\" + operation + \"\\\",\" +\n                \"\\\"recordStatus\\\":\\\"\" + recordStatus + \"\\\",\" +\n                \"\\\"changedData\\\":\" + changedData + \",\" +\n                \"\\\"cost(ms)\\\":\" + cost + \"}\";\n        }\n    }\n\n    @Data\n    public static class Columns2SelectItemsResult {\n\n        private Column pk;\n        /**\n         * all column with additional columns: ID, etc.\n         */\n        private List<SelectItem<?>> selectItems;\n        /**\n         * newly added column count from meta data.\n         */\n        private int additionalItemCount;\n\n        public static Columns2SelectItemsResult build(List<SelectItem<?>> selectItems, int additionalItemCount) {\n            Columns2SelectItemsResult result = new Columns2SelectItemsResult();\n            result.setSelectItems(selectItems);\n            result.setAdditionalItemCount(additionalItemCount);\n            return result;\n        }\n    }\n\n    @Data\n    public static class OriginalDataObj {\n\n        private List<DataChangedRecord> originalDataObj;\n\n        public boolean isEmpty() {\n            return originalDataObj == null || originalDataObj.isEmpty();\n        }\n\n    }\n\n    @Data\n    public static class DataColumnChangeResult {\n\n        private String columnName;\n        private Object originalValue;\n        private Object updateValue;\n\n        @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n        public boolean isDataChanged(Object updateValue) {\n            if (Objects.equals(originalValue, updateValue)) {\n                return false;\n            }\n            if (originalValue instanceof Clob) {\n                String originalStr = convertClob((Clob) originalValue);\n                setOriginalValue(originalStr);\n                return !originalStr.equals(updateValue);\n            }\n            if (originalValue instanceof Comparable) {\n                Comparable original = (Comparable) originalValue;\n                if (original.getClass().isInstance(updateValue)) {\n                    Comparable update = (Comparable) updateValue;\n                    return original.compareTo(update) != 0;\n                }\n            }\n            return true;\n        }\n\n        public static String convertClob(Clob clobObj) {\n            try {\n                return clobObj.getSubString(0, (int) clobObj.length());\n            } catch (Exception e) {\n                try (Reader is = clobObj.getCharacterStream()) {\n                    char[] chars = new char[64];\n                    int readChars;\n                    StringBuilder sb = new StringBuilder();\n                    while ((readChars = is.read(chars)) != -1) {\n                        sb.append(chars, 0, readChars);\n                    }\n                    return sb.toString();\n                } catch (Exception e2) {\n                    //ignored\n                    return \"unknown clobObj\";\n                }\n            }\n        }\n\n        public static DataColumnChangeResult constrcutByUpdateVal(String columnName, Object updateValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setUpdateValue(updateValue);\n            return res;\n        }\n\n        public static DataColumnChangeResult constrcutByOriginalVal(String columnName, Object originalValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setOriginalValue(originalValue);\n            return res;\n        }\n\n        public String generateDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"\\\"\").append(columnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(originalValue)).append(\"->\").append(convertDoubleQuotes(updateValue)).append(\"\\\"\").append(\",\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    @Data\n    public static class DataChangedRecord {\n\n        private String pkColumnName;\n        private Object pkColumnVal;\n        private List<DataColumnChangeResult> originalColumnDatas;\n        private List<DataColumnChangeResult> updatedColumns;\n\n        public boolean hasUpdate(Map<String, Object> columnNameValMap, Set<String> ignoredColumns, Set<String> ignoreAllColumns) {\n            if (originalColumnDatas == null) {\n                return true;\n            }\n            boolean hasUpdate = false;\n            updatedColumns = new ArrayList<>(originalColumnDatas.size());\n            for (DataColumnChangeResult originalColumn : originalColumnDatas) {\n                final String columnName = originalColumn.getColumnName().toUpperCase();\n                if (ignoredColumns != null && ignoredColumns.contains(columnName) || ignoreAllColumns.contains(columnName)) {\n                    continue;\n                }\n                Object updatedValue = columnNameValMap.get(columnName);\n                if (originalColumn.isDataChanged(updatedValue)) {\n                    hasUpdate = true;\n                    originalColumn.setUpdateValue(updatedValue);\n                    updatedColumns.add(originalColumn);\n                }\n            }\n            return hasUpdate;\n        }\n\n        public String generateUpdatedDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"{\");\n            if (pkColumnName != null) {\n                sb.append(\"\\\"\").append(pkColumnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(pkColumnVal)).append(\"\\\"\").append(\",\");\n            }\n            for (DataColumnChangeResult update : updatedColumns) {\n                sb.append(update.generateDataStr());\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"}\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    public static class DataUpdateLimitationException extends MybatisPlusException {\n\n        public DataUpdateLimitationException(String message) {\n            super(message);\n        }\n\n        public static DataUpdateLimitationException DEFAULT = new DataUpdateLimitationException(\"本次操作 因超过系统安全阈值 被拦截，如需继续，请联系管理员!\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.select.WithItem;\nimport net.sf.jsqlparser.statement.update.Update;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class DataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private DataPermissionHandler dataPermissionHandler;\n\n    @SuppressWarnings(\"RedundantThrows\")\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            // 参照 com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.processSelect 做的修改\n            final String whereSegment = (String) obj;\n            processSelectBody(select, whereSegment);\n            List<WithItem<?>> withItemsList = select.getWithItemsList();\n            if (!CollectionUtils.isEmpty(withItemsList)) {\n                withItemsList.forEach(withItem -> processSelectBody(withItem.getSelect(), whereSegment));\n            }\n        } else {\n            // 兼容原来的旧版 DataPermissionHandler 场景\n            if (select instanceof PlainSelect) {\n                this.setWhere((PlainSelect) select, (String) obj);\n            } else if (select instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) select;\n                List<Select> selectBodyList = setOperationList.getSelects();\n                selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));\n            }\n        }\n    }\n\n    /**\n     * 设置 where 条件\n     *\n     * @param plainSelect  查询对象\n     * @param whereSegment 查询条件片段\n     */\n    protected void setWhere(PlainSelect plainSelect, String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        // 兼容旧版的数据权限处理\n        final Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), whereSegment);\n        if (null != sqlSegment) {\n            plainSelect.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(update.getTable(), update.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            update.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(delete.getTable(), delete.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            delete.setWhere(sqlSegment);\n        }\n    }\n\n    protected Expression getUpdateOrDeleteExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            return andExpression(table, where, whereSegment);\n        } else {\n            // 兼容旧版的数据权限处理\n            return dataPermissionHandler.getSqlSegment(where, whereSegment);\n        }\n    }\n\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        // 只有新版数据权限处理器才会执行到这里\n        final MultiDataPermissionHandler handler = (MultiDataPermissionHandler) dataPermissionHandler;\n        return handler.getSqlSegment(table, where, whereSegment);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.DynamicTableNameHandler;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.UnsupportedStatement;\n\n/**\n * 动态表处理器 (基于JsqlParser解析器)\n * <p>默认情况下,如果JsqlParser解析不了,则调用父类的解析进行处理</p>\n *\n * @author nieqiurong\n * @see DynamicTableNameHandler\n * @since 3.5.11\n */\n@Setter\npublic class DynamicTableNameJsqlParserInnerInterceptor extends DynamicTableNameInnerInterceptor {\n\n    /**\n     * 是否忽略解析异常\n     */\n    private boolean ignoreException = false;\n\n    /**\n     * 当JsqlParser无法解析语句时是否进行调用父类继续解析处理\n     *\n     * @see com.baomidou.mybatisplus.core.toolkit.TableNameParser\n     */\n    private boolean shouldFallback = true;\n\n\n    @Deprecated\n    public DynamicTableNameJsqlParserInnerInterceptor() {\n    }\n\n    public DynamicTableNameJsqlParserInnerInterceptor(TableNameHandler tableNameHandler) {\n        super(tableNameHandler);\n    }\n\n    @Override\n    protected String processTableName(String sql) {\n        boolean unsupported = false;\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            statement.accept(new DynamicTableNameHandler(sql, super.getTableNameHandler()));\n            if (statement instanceof UnsupportedStatement) {\n                unsupported = true;\n                return super.processTableName(sql);\n            }\n            return statement.toString();\n        } catch (Exception exception) {\n            return handleFallback(unsupported, sql, exception);\n        }\n    }\n\n    private String handleFallback(boolean unsupported, String sql, Exception originalException) {\n        Exception exception = originalException;\n        if (!unsupported || shouldFallback) {\n            try {\n                return super.processTableName(sql);\n            } catch (Exception e) {\n                exception = e;\n            }\n        }\n        if (ignoreException) {\n            return sql;\n        }\n        throw new MybatisPlusException(\"Table name processing failed : \", exception);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/IllegalSQLInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.EncryptUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Function;\nimport net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.Join;\nimport net.sf.jsqlparser.statement.select.ParenthesedSelect;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 由于开发人员水平参差不齐，即使订了开发规范很多人也不遵守\n * <p>SQL是影响系统性能最重要的因素，所以拦截掉垃圾SQL语句</p>\n * <br>\n * <p>拦截SQL类型的场景</p>\n * <p>1.必须使用到索引，包含left join连接字段，符合索引最左原则</p>\n * <p>必须使用索引好处，</p>\n * <p>1.1 如果因为动态SQL，bug导致update的where条件没有带上，全表更新上万条数据</p>\n * <p>1.2 如果检查到使用了索引，SQL性能基本不会太差</p>\n * <br>\n * <p>2.SQL尽量单表执行，有查询left join的语句，必须在注释里面允许该SQL运行，否则会被拦截，有left join的语句，如果不能拆成单表执行的SQL，请leader商量在做</p>\n * <p>https://gaoxianglong.github.io/shark</p>\n * <p>SQL尽量单表执行的好处</p>\n * <p>2.1 查询条件简单、易于开理解和维护；</p>\n * <p>2.2 扩展性极强；（可为分库分表做准备）</p>\n * <p>2.3 缓存利用率高；</p>\n * <p>2.在字段上使用函数</p>\n * <br>\n * <p>3.where条件为空</p>\n * <p>4.where条件使用了 !=</p>\n * <p>5.where条件使用了 not 关键字</p>\n * <p>6.where条件使用了 or 关键字</p>\n * <p>7.where条件使用了 使用子查询</p>\n *\n * @author willenfoo\n * @deprecated 3.5.10 实用性不高,语法分析太差,计划移除\n * @since 3.4.0\n */\n@Deprecated\npublic class IllegalSQLInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 缓存验证结果，提高性能\n     */\n    private static final Set<String> cacheValidResult = new HashSet<>();\n    /**\n     * 缓存表的索引信息\n     */\n    private static final Map<String, List<IndexInfo>> indexInfoMap = new ConcurrentHashMap<>();\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpStatementHandler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || InterceptorIgnoreHelper.willIgnoreIllegalSql(ms.getId())) {\n            return;\n        }\n        BoundSql boundSql = mpStatementHandler.boundSql();\n        String originalSql = boundSql.getSql();\n        logger.debug(\"检查SQL是否合规，SQL:\" + originalSql);\n        String md5Base64 = EncryptUtils.md5Base64(originalSql);\n        if (cacheValidResult.contains(md5Base64)) {\n            logger.debug(\"该SQL已验证，无需再次验证，，SQL:\" + originalSql);\n            return;\n        }\n        parserSingle(originalSql, connection);\n        //缓存验证结果\n        cacheValidResult.add(md5Base64);\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (select instanceof PlainSelect) {\n            PlainSelect plainSelect = (PlainSelect) select;\n            FromItem fromItem = ((PlainSelect) select).getFromItem();\n            while (fromItem instanceof ParenthesedSelect) {\n                ParenthesedSelect parenthesedSelect = (ParenthesedSelect) fromItem;\n                plainSelect = (PlainSelect) parenthesedSelect.getSelect();\n                fromItem = plainSelect.getFromItem();\n            }\n            Expression where = plainSelect.getWhere();\n            Assert.notNull(where, \"非法SQL，必须要有where条件\");\n            Table table = (Table) plainSelect.getFromItem();\n            List<Join> joins = plainSelect.getJoins();\n            validWhere(where, table, (Connection) obj);\n            validJoins(joins, table, (Connection) obj);\n        }\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        Expression where = update.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = update.getTable();\n        List<Join> joins = update.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        Expression where = delete.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = delete.getTable();\n        List<Join> joins = delete.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    /**\n     * 验证expression对象是不是 or、not等等\n     *\n     * @param expression ignore\n     */\n    private void validExpression(Expression expression) {\n        while (expression instanceof ParenthesedExpressionList) {\n            ParenthesedExpressionList<Expression> parenthesis = (ParenthesedExpressionList) expression;\n            expression = parenthesis.get(0);\n        }\n        //where条件使用了 or 关键字\n        if (expression instanceof OrExpression) {\n            OrExpression orExpression = (OrExpression) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【or】关键字，错误or信息：\" + orExpression.toString());\n        } else if (expression instanceof NotEqualsTo) {\n            NotEqualsTo notEqualsTo = (NotEqualsTo) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【!=】关键字，错误!=信息：\" + notEqualsTo.toString());\n        } else if (expression instanceof BinaryExpression) {\n            BinaryExpression binaryExpression = (BinaryExpression) expression;\n            // TODO 升级 jsqlparser 后待实现\n//            if (binaryExpression.isNot()) {\n//                throw new MybatisPlusException(\"非法SQL，where条件中不能使用【not】关键字，错误not信息：\" + binaryExpression.toString());\n//            }\n            if (binaryExpression.getLeftExpression() instanceof Function) {\n                Function function = (Function) binaryExpression.getLeftExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用数据库函数，错误函数信息：\" + function.toString());\n            }\n            if (binaryExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) binaryExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        } else if (expression instanceof InExpression) {\n            InExpression inExpression = (InExpression) expression;\n            if (inExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) inExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        }\n\n    }\n\n    /**\n     * 如果SQL用了 left Join，验证是否有or、not等等，并且验证是否使用了索引\n     *\n     * @param joins      ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validJoins(List<Join> joins, Table table, Connection connection) {\n        //允许执行join，验证jion是否使用索引等等\n        if (joins != null) {\n            for (Join join : joins) {\n                Table rightTable = (Table) join.getRightItem();\n                Collection<Expression> onExpressions = join.getOnExpressions();\n                for (Expression expression : onExpressions) {\n                    validWhere(expression, table, rightTable, connection);\n                }\n            }\n        }\n    }\n\n    /**\n     * 检查是否使用索引\n     *\n     * @param table      ignore\n     * @param columnName ignore\n     * @param connection ignore\n     */\n    private void validUseIndex(Table table, String columnName, Connection connection) {\n        //是否使用索引\n        boolean useIndexFlag = false;\n        if (StringUtils.isNotBlank(columnName)) {\n            //表存在的索引\n            String dbName = table.getSchemaName();\n            String tableName = table.getName();\n            String catalogName = table.getCatalogName();\n            columnName = SqlParserUtils.removeWrapperSymbol(columnName);\n            List<IndexInfo> indexInfos = getIndexInfos(null, catalogName, dbName, tableName, connection);\n            for (IndexInfo indexInfo : indexInfos) {\n                if (indexInfo.getColumnName().equalsIgnoreCase(columnName)) {\n                    useIndexFlag = true;\n                    break;\n                }\n            }\n        }\n        if (!useIndexFlag) {\n            throw new MybatisPlusException(\"非法SQL，SQL未使用到索引, table:\" + table.getName() + \", columnName:\" + columnName);\n        }\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Connection connection) {\n        validWhere(expression, table, null, connection);\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param joinTable  ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Table joinTable, Connection connection) {\n        validExpression(expression);\n        if (expression instanceof BinaryExpression) {\n            //获得左边表达式\n            Expression leftExpression = ((BinaryExpression) expression).getLeftExpression();\n            validExpression(leftExpression);\n\n            //如果左边表达式为Column对象，则直接获得列名\n            if (leftExpression instanceof Column) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                if (joinTable != null && rightExpression instanceof Column) {\n                    if (Objects.equals(((Column) rightExpression).getTable().getName(), table.getAlias().getName())) {\n                        validUseIndex(table, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(joinTable, ((Column) leftExpression).getColumnName(), connection);\n                    } else {\n                        validUseIndex(joinTable, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                    }\n                } else {\n                    //获得列名\n                    validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                }\n            }\n            //如果BinaryExpression，进行迭代\n            else if (leftExpression instanceof BinaryExpression) {\n                validWhere(leftExpression, table, joinTable, connection);\n            }\n\n            //获得右边表达式，并分解\n            if (joinTable != null) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                validExpression(rightExpression);\n            }\n        }\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param dbName    数据库名\n     * @param tableName 表名\n     * @param conn      数据库连接\n     * @return 索引信息\n     * @see #getIndexInfos(String, String, String, String, Connection)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public List<IndexInfo> getIndexInfos(String dbName, String tableName, Connection conn) {\n        return getIndexInfos(null, dbName, tableName, conn);\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key       缓存key\n     * @param dbName    数据库名\n     * @param tableName 表名\n     * @param conn      数据库连接\n     * @return 索引信息\n     * @see #getIndexInfos(String, String, String, String, Connection)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public List<IndexInfo> getIndexInfos(String key, String dbName, String tableName, Connection conn) {\n        return getIndexInfos(key, null, dbName, tableName, conn);\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key         缓存key\n     * @param catalogName catalogName\n     * @param dbName      数据库名\n     * @param tableName   表名\n     * @param conn        数据库连接\n     * @return 索引信息\n     * @since 3.5.11\n     */\n    public List<IndexInfo> getIndexInfos(String key, String catalogName, String dbName, String tableName, Connection conn) {\n        List<IndexInfo> indexInfos = null;\n        if (StringUtils.isNotBlank(key)) {\n            indexInfos = indexInfoMap.get(key);\n        }\n        if (indexInfos == null || indexInfos.isEmpty()) {\n            ResultSet rs;\n            try {\n                DatabaseMetaData metadata = conn.getMetaData();\n                String catalog = StringUtils.isBlank(catalogName) ? conn.getCatalog() : catalogName;\n                String schema = StringUtils.isBlank(dbName) ? conn.getSchema() : dbName;\n                rs = metadata.getIndexInfo(catalog, schema, SqlParserUtils.removeWrapperSymbol(tableName), false, true);\n                indexInfos = new ArrayList<>();\n                while (rs.next()) {\n                    //索引中的列序列号等于1，才有效\n                    if (Objects.equals(rs.getString(8), \"1\")) {\n                        IndexInfo indexInfo = new IndexInfo();\n                        indexInfo.setDbName(rs.getString(1));\n                        indexInfo.setTableName(rs.getString(3));\n                        indexInfo.setColumnName(rs.getString(9));\n                        indexInfos.add(indexInfo);\n                    }\n                }\n                if (StringUtils.isNotBlank(key)) {\n                    indexInfoMap.put(key, indexInfos);\n                }\n            } catch (SQLException e) {\n                logger.error(String.format(\"getIndexInfo fault, with key:%s, dbName:%s, tableName:%s\", key, dbName, tableName), e);\n            }\n        }\n        return indexInfos;\n    }\n\n    /**\n     * 索引对象\n     */\n    @Data\n    private static class IndexInfo {\n\n        private String dbName;\n\n        private String tableName;\n\n        private String columnName;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/PaginationInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;\nimport com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\nimport org.apache.ibatis.cache.CacheKey;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\n/**\n * 分页拦截器\n * <p>\n * 默认对 left join 进行优化,虽然能优化count,但是加上分页的话如果1对多本身结果条数就是不正确的\n *\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@SuppressWarnings({\"rawtypes\"})\npublic class PaginationInnerInterceptor implements InnerInterceptor {\n    /**\n     * 获取jsqlparser中count的SelectItem\n     */\n    protected static final List<SelectItem<?>> COUNT_SELECT_ITEM = Collections.singletonList(\n        new SelectItem<>(new Column().withColumnName(\"COUNT(*)\")).withAlias(new Alias(\"total\"))\n    );\n    protected static final Map<String, MappedStatement> countMsCache = new ConcurrentHashMap<>();\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n\n    /**\n     * 溢出总页数后是否进行处理\n     */\n    protected boolean overflow;\n    /**\n     * 单页分页条数限制\n     */\n    protected Long maxLimit;\n    /**\n     * 数据库类型\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private DbType dbType;\n    /**\n     * 方言实现类\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private IDialect dialect;\n    /**\n     * 生成 countSql 优化掉 join\n     * 现在只支持 left join\n     *\n     * @since 3.4.2\n     */\n    protected boolean optimizeJoin = true;\n\n    public PaginationInnerInterceptor(DbType dbType) {\n        this.dbType = dbType;\n    }\n\n    public PaginationInnerInterceptor(IDialect dialect) {\n        this.dialect = dialect;\n    }\n\n    /**\n     * 这里进行count,如果count为0这返回false(就是不再执行sql了)\n     */\n    @Override\n    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {\n            return true;\n        }\n\n        BoundSql countSql;\n        MappedStatement countMs = buildCountMappedStatement(ms, page.countId());\n        if (countMs != null) {\n            countSql = countMs.getBoundSql(parameter);\n        } else {\n            countMs = buildAutoCountMappedStatement(ms);\n            String countSqlStr = autoCountSql(page, boundSql.getSql());\n            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n            countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);\n            PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());\n        }\n\n        CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);\n        List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);\n        long total = 0;\n        if (CollectionUtils.isNotEmpty(result)) {\n            // 个别数据库 count 没数据不会返回 0\n            Object o = result.get(0);\n            if (o != null) {\n                total = Long.parseLong(o.toString());\n            }\n        }\n        page.setTotal(total);\n        return continuePage(page);\n    }\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (null == page) {\n            return;\n        }\n\n        // 处理 orderBy 拼接\n        boolean addOrdered = false;\n        String buildSql = boundSql.getSql();\n        List<OrderItem> orders = page.orders();\n        if (CollectionUtils.isNotEmpty(orders)) {\n            addOrdered = true;\n            buildSql = this.concatOrderBy(buildSql, orders);\n        }\n\n        // size 小于 0 且不限制返回值则不构造分页sql\n        Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;\n        if (page.getSize() < 0 && null == _limit) {\n            if (addOrdered) {\n                PluginUtils.mpBoundSql(boundSql).sql(buildSql);\n            }\n            return;\n        }\n\n        handlerLimit(page, _limit);\n        IDialect dialect = findIDialect(executor);\n\n        final Configuration configuration = ms.getConfiguration();\n        DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n\n        List<ParameterMapping> mappings = mpBoundSql.parameterMappings();\n        Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();\n        model.consumers(mappings, configuration, additionalParameter);\n        mpBoundSql.sql(model.getDialectSql());\n        mpBoundSql.parameterMappings(mappings);\n    }\n\n    /**\n     * 获取分页方言类的逻辑\n     *\n     * @param executor Executor\n     * @return 分页方言类\n     */\n    protected IDialect findIDialect(Executor executor) {\n        if (dialect != null) {\n            return dialect;\n        }\n        if (dbType != null) {\n            dialect = DialectFactory.getDialect(dbType);\n            return dialect;\n        }\n        return DialectFactory.getDialect(JdbcUtils.getDbType(executor));\n    }\n\n    /**\n     * 获取指定的 id 的 MappedStatement\n     *\n     * @param ms      MappedStatement\n     * @param countId id\n     * @return MappedStatement\n     */\n    protected MappedStatement buildCountMappedStatement(MappedStatement ms, String countId) {\n        if (StringUtils.isNotBlank(countId)) {\n            final String id = ms.getId();\n            if (!countId.contains(StringPool.DOT)) {\n                countId = id.substring(0, id.lastIndexOf(StringPool.DOT) + 1) + countId;\n            }\n            final Configuration configuration = ms.getConfiguration();\n            try {\n                return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> configuration.getMappedStatement(key, false));\n            } catch (Exception e) {\n                logger.warn(String.format(\"can not find this countId: [\\\"%s\\\"]\", countId));\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 构建 mp 自用自动的 MappedStatement\n     *\n     * @param ms MappedStatement\n     * @return MappedStatement\n     */\n    protected MappedStatement buildAutoCountMappedStatement(MappedStatement ms) {\n        final String countId = ms.getId() + \"_mpCount\";\n        final Configuration configuration = ms.getConfiguration();\n        return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> {\n            MappedStatement.Builder builder = new MappedStatement.Builder(configuration, key, ms.getSqlSource(), ms.getSqlCommandType());\n            builder.resource(ms.getResource());\n            builder.fetchSize(ms.getFetchSize());\n            builder.statementType(ms.getStatementType());\n            builder.timeout(ms.getTimeout());\n            builder.parameterMap(ms.getParameterMap());\n            builder.resultMaps(Collections.singletonList(new ResultMap.Builder(configuration, Constants.MYBATIS_PLUS, Long.class, Collections.emptyList()).build()));\n            builder.resultSetType(ms.getResultSetType());\n            builder.cache(ms.getCache());\n            builder.flushCacheRequired(ms.isFlushCacheRequired());\n            builder.useCache(ms.isUseCache());\n            return builder.build();\n        });\n    }\n\n    /**\n     * 获取自动优化的 countSql\n     *\n     * @param page 参数\n     * @param sql  sql\n     * @return countSql\n     */\n    public String autoCountSql(IPage<?> page, String sql) {\n        if (!page.optimizeCountSql()) {\n            return lowLevelCountSql(sql);\n        }\n        try {\n            Select select = (Select) JsqlParserGlobal.parse(sql);\n            // https://github.com/baomidou/mybatis-plus/issues/3920  分页增加union语法支持\n            if (select instanceof SetOperationList) {\n                return lowLevelCountSql(sql);\n            }\n            PlainSelect plainSelect = (PlainSelect) select;\n\n            // 优化 order by 在非分组情况下\n            List<OrderByElement> orderBy = plainSelect.getOrderByElements();\n            if (CollectionUtils.isNotEmpty(orderBy)) {\n                boolean canClean = true;\n                for (OrderByElement order : orderBy) {\n                    // order by 里带参数,不去除order by\n                    Expression expression = order.getExpression();\n                    if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {\n                        canClean = false;\n                        break;\n                    }\n                }\n                if (canClean) {\n                    plainSelect.setOrderByElements(null);\n                }\n            }\n            Distinct distinct = plainSelect.getDistinct();\n            GroupByElement groupBy = plainSelect.getGroupBy();\n            // 包含 distinct、groupBy 不优化\n            if (null != distinct || null != groupBy) {\n                return lowLevelCountSql(select.toString());\n            }\n            //#95 Github, selectItems contains #{} ${}, which will be translated to ?, and it may be in a function: power(#{myInt},2)\n            for (SelectItem item : plainSelect.getSelectItems()) {\n                if (item.toString().contains(StringPool.QUESTION_MARK)) {\n                    return lowLevelCountSql(select.toString());\n                }\n            }\n\n            // 包含 join 连表,进行判断是否移除 join 连表\n            if (optimizeJoin && page.optimizeJoinOfCountSql()) {\n                List<Join> joins = plainSelect.getJoins();\n                if (CollectionUtils.isNotEmpty(joins)) {\n                    boolean canRemoveJoin = true;\n                    String whereS = Optional.ofNullable(plainSelect.getWhere()).map(Expression::toString).orElse(StringPool.EMPTY);\n                    // 不区分大小写\n                    whereS = whereS.toLowerCase();\n                    for (Join join : joins) {\n                        if (!join.isLeft()) {\n                            canRemoveJoin = false;\n                            break;\n                        }\n                        FromItem rightItem = join.getRightItem();\n                        String str = \"\";\n                        if (rightItem instanceof Table) {\n                            Table table = (Table) rightItem;\n                            str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;\n                        } else if (rightItem instanceof ParenthesedSelect) {\n                            ParenthesedSelect subSelect = (ParenthesedSelect) rightItem;\n                            /* 如果 left join 是子查询，并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {\n                                canRemoveJoin = false;\n                                break;\n                            }\n                            str = subSelect.getAlias().getName() + StringPool.DOT;\n                        }\n                        // 不区分大小写\n                        str = str.toLowerCase();\n\n                        if (whereS.contains(str)) {\n                            /* 如果 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            canRemoveJoin = false;\n                            break;\n                        }\n\n                        for (Expression expression : join.getOnExpressions()) {\n                            if (expression.toString().contains(StringPool.QUESTION_MARK)) {\n                                /* 如果 join 里包含 ?(代表有入参) 就不移除 join */\n                                canRemoveJoin = false;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (canRemoveJoin) {\n                        plainSelect.setJoins(null);\n                    }\n                }\n            }\n\n            // 优化 SQL\n            plainSelect.setSelectItems(COUNT_SELECT_ITEM);\n            return select.toString();\n        } catch (JSQLParserException e) {\n            // 无法优化使用原 SQL\n            logger.warn(\"optimize this sql to a count sql has exception, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"optimize this sql to a count sql has error, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e);\n        }\n        return lowLevelCountSql(sql);\n    }\n\n    /**\n     * 无法进行count优化时,降级使用此方法\n     *\n     * @param originalSql 原始sql\n     * @return countSql\n     */\n    protected String lowLevelCountSql(String originalSql) {\n        return SqlParserUtils.getOriginalCountSql(originalSql);\n    }\n\n    /**\n     * 查询SQL拼接Order By\n     *\n     * @param originalSql 需要拼接的SQL\n     * @return ignore\n     */\n    public String concatOrderBy(String originalSql, List<OrderItem> orderList) {\n        try {\n            Select selectBody = (Select) JsqlParserGlobal.parse(originalSql);\n            if (selectBody instanceof PlainSelect) {\n                PlainSelect plainSelect = (PlainSelect) selectBody;\n                List<OrderByElement> orderByElements = plainSelect.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                plainSelect.setOrderByElements(orderByElementsReturn);\n                return plainSelect.toString();\n            } else if (selectBody instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) selectBody;\n                List<OrderByElement> orderByElements = setOperationList.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                setOperationList.setOrderByElements(orderByElementsReturn);\n                return setOperationList.toString();\n            }  else {\n                return originalSql;\n            }\n        } catch (JSQLParserException e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e);\n        }\n        return originalSql;\n    }\n\n    protected List<OrderByElement> addOrderByElements(List<OrderItem> orderList, List<OrderByElement> orderByElements) {\n        List<OrderByElement> additionalOrderBy = orderList.stream()\n            .filter(item -> StringUtils.isNotBlank(item.getColumn()))\n            .map(item -> {\n                OrderByElement element = new OrderByElement();\n                element.setExpression(new Column(item.getColumn()));\n                element.setAsc(item.isAsc());\n                element.setAscDescPresent(true);\n                return element;\n            }).collect(Collectors.toList());\n        if (CollectionUtils.isEmpty(orderByElements)) {\n            return additionalOrderBy;\n        }\n        // github pull/3550 优化排序，比如：默认 order by id 前端传了name排序，设置为 order by name,id\n        additionalOrderBy.addAll(orderByElements);\n        return additionalOrderBy;\n    }\n\n    /**\n     * count 查询之后,是否继续执行分页\n     *\n     * @param page 分页对象\n     * @return 是否\n     */\n    protected boolean continuePage(IPage<?> page) {\n        if (page.getTotal() <= 0) {\n            return false;\n        }\n        if (page.getCurrent() > page.getPages()) {\n            if (overflow) {\n                //溢出总页数处理\n                handlerOverflow(page);\n            } else {\n                // 超过最大范围，未设置溢出逻辑中断 list 执行\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 处理超出分页条数限制,默认归为限制数\n     *\n     * @param page IPage\n     */\n    protected void handlerLimit(IPage<?> page, Long limit) {\n        final long size = page.getSize();\n        if (limit != null && limit > 0 && (size > limit || size < 0)) {\n            page.setSize(limit);\n        }\n    }\n\n    /**\n     * 处理页数溢出,默认设置为第一页\n     *\n     * @param page IPage\n     */\n    protected void handlerOverflow(IPage<?> page) {\n        page.setCurrent(1);\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties)\n            .whenNotBlank(\"overflow\", Boolean::parseBoolean, this::setOverflow)\n            .whenNotBlank(\"dbType\", DbType::getDbType, this::setDbType)\n            .whenNotBlank(\"dialect\", ClassUtils::newInstance, this::setDialect)\n            .whenNotBlank(\"maxLimit\", Long::parseLong, this::setMaxLimit)\n            .whenNotBlank(\"optimizeJoin\", Boolean::parseBoolean, this::setOptimizeJoin);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport lombok.*;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.*;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class TenantLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private TenantLineHandler tenantLineHandler;\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), null));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), null));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        final String whereSegment = (String) obj;\n        processSelectBody(select, whereSegment);\n        List<WithItem<?>> withItemsList = select.getWithItemsList();\n        if (!CollectionUtils.isEmpty(withItemsList)) {\n            withItemsList.forEach(withItem -> processSelectBody(withItem.getSelect(), whereSegment));\n        }\n    }\n\n    @Override\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(insert.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<Column> columns = insert.getColumns();\n        if (CollectionUtils.isEmpty(columns)) {\n            // 针对不给列名的insert 不处理\n            return;\n        }\n        String tenantIdColumn = tenantLineHandler.getTenantIdColumn();\n        if (tenantLineHandler.ignoreInsert(columns, tenantIdColumn)) {\n            // 针对已给出租户列的insert 不处理\n            return;\n        }\n        columns.add(new Column(tenantIdColumn));\n        Expression tenantId = tenantLineHandler.getTenantId();\n        // fixed gitee pulls/141 duplicate update\n        List<UpdateSet> duplicateUpdateColumns = insert.getDuplicateUpdateSets();\n        if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {\n            duplicateUpdateColumns.add(new UpdateSet(new Column(tenantIdColumn), tenantId));\n        }\n\n        Select select = insert.getSelect();\n        if (select instanceof PlainSelect) { //fix github issue 4998  修复升级到4.5版本的问题\n            this.processInsertSelect(select, (String) obj);\n        } else if (insert.getValues() != null) {\n            // fixed github pull/295\n            Values values = insert.getValues();\n            ExpressionList<Expression> expressions = (ExpressionList<Expression>) values.getExpressions();\n            if (expressions instanceof ParenthesedExpressionList) {\n                expressions.addExpression(tenantId);\n            } else {\n                if (CollectionUtils.isNotEmpty(expressions)) {//fix github issue 4998 jsqlparse 4.5 批量insert ItemsList不是MultiExpressionList 了，需要特殊处理\n                    int len = expressions.size();\n                    for (int i = 0; i < len; i++) {\n                        Expression expression = expressions.get(i);\n                        if (expression instanceof ParenthesedExpressionList) {\n                            ((ParenthesedExpressionList<Expression>) expression).addExpression(tenantId);\n                        } else {\n                            expressions.add(tenantId);\n                        }\n                    }\n                } else {\n                    expressions.add(tenantId);\n                }\n            }\n        } else {\n            throw ExceptionUtils.mpe(\"Failed to process multiple-table update, please exclude the tableName or statementId\");\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Table table = update.getTable();\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<UpdateSet> sets = update.getUpdateSets();\n        if (!CollectionUtils.isEmpty(sets)) {\n            sets.forEach(us -> us.getValues().forEach(ex -> {\n                if (ex instanceof Select) {\n                    processSelectBody(((Select) ex), (String) obj);\n                }\n            }));\n        }\n        update.setWhere(this.andExpression(table, update.getWhere(), (String) obj));\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(delete.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere(), (String) obj));\n    }\n\n    /**\n     * 处理 insert into select\n     * <p>\n     * 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了\n     *\n     * @param selectBody SelectBody\n     */\n    protected void processInsertSelect(Select selectBody, final String whereSegment) {\n        if(selectBody instanceof PlainSelect){\n            PlainSelect plainSelect = (PlainSelect) selectBody;\n            FromItem fromItem = plainSelect.getFromItem();\n            if (fromItem instanceof Table) {\n                // fixed gitee pulls/141 duplicate update\n                processPlainSelect(plainSelect, whereSegment);\n                appendSelectItem(plainSelect.getSelectItems());\n            } else if (fromItem instanceof Select) {\n                Select subSelect = (Select) fromItem;\n                appendSelectItem(plainSelect.getSelectItems());\n                processInsertSelect(subSelect, whereSegment);\n            }\n        } else if(selectBody instanceof ParenthesedSelect){\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processInsertSelect(parenthesedSelect.getSelect(), whereSegment);\n\n        }\n    }\n\n    /**\n     * 追加 SelectItem\n     *\n     * @param selectItems SelectItem\n     */\n    protected void appendSelectItem(List<SelectItem<?>> selectItems) {\n        if (CollectionUtils.isEmpty(selectItems)) {\n            return;\n        }\n        if (selectItems.size() == 1) {\n            SelectItem item = selectItems.get(0);\n            Expression expression = item.getExpression();\n            if (expression instanceof AllColumns) {\n                return;\n            }\n        }\n        selectItems.add(new SelectItem<>(new Column(tenantLineHandler.getTenantIdColumn())));\n    }\n\n    /**\n     * 租户字段别名设置\n     * <p>tenantId 或 tableAlias.tenantId</p>\n     *\n     * @param table 表对象\n     * @return 字段\n     */\n    protected Column getAliasColumn(Table table) {\n        StringBuilder column = new StringBuilder();\n        // todo 该起别名就要起别名,禁止修改此处逻辑\n        if (table.getAlias() != null) {\n            column.append(table.getAlias().getName()).append(StringPool.DOT);\n        }\n        column.append(tenantLineHandler.getTenantIdColumn());\n        return new Column(column.toString());\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties).whenNotBlank(\"tenantLineHandler\",\n            ClassUtils::newInstance, this::setTenantLineHandler);\n    }\n\n    /**\n     * 构建租户条件表达式\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径（在原租户拦截器功能中，这个参数并不需要参与相关判断）\n     * @return 租户条件表达式\n     * @see BaseMultiTableInnerInterceptor#buildTableExpression(Table, Expression, String)\n     */\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            return null;\n        }\n        return new EqualsTo(getAliasColumn(table), tenantLineHandler.getTenantId());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/JSqlParserTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL 解析测试\n */\nclass JSqlParserTest {\n\n    @Test\n    void parser() throws Exception {\n        Select select = (Select) CCJSqlParserUtil.parse(\"SELECT a,b,c FROM tableName t WHERE t.col = 9 and b=c LIMIT 3, ?\");\n\n        PlainSelect ps = (PlainSelect) select;\n\n        System.out.println(ps.getWhere().toString());\n        System.out.println(ps.getSelectItems().get(1).toString());\n\n        AndExpression e = (AndExpression) ps.getWhere();\n        System.out.println(e.getLeftExpression());\n    }\n\n    @Test\n    void testDecr() throws JSQLParserException {\n        // 如果连一起 SqlParser 将无法解析 , 还有种处理方式就自减为负数的时候 转为 自增.\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a --110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a - -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a - -110\", parse2.toString());\n    }\n\n    @Test\n    void testIncr() throws JSQLParserException {\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a +-110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a + -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse2.toString());\n    }\n\n    @Test\n    void notLikeParser() throws Exception {\n        final String targetSql = \"SELECT * FROM tableName WHERE id NOT LIKE ?\";\n        Select select = (Select) CCJSqlParserUtil.parse(targetSql);\n        assertThat(select.toString()).isEqualTo(targetSql);\n    }\n\n    @Test\n    void updateWhereParser() throws Exception {\n        Update update = (Update) CCJSqlParserUtil.parse(\"Update tableName t SET t.a=(select c from tn where tn.id=t.id),b=2,c=3 \");\n        Assertions.assertNull(update.getWhere());\n    }\n\n    @Test\n    void deleteWhereParser() throws Exception {\n        Delete delete = (Delete) CCJSqlParserUtil.parse(\"delete from tableName t\");\n        Assertions.assertNull(delete.getWhere());\n    }\n\n    @Test\n    void testSelectForUpdate() throws Exception {\n        Assertions.assertEquals(\"SELECT * FROM t_demo WHERE a = 1 FOR UPDATE\",\n            CCJSqlParserUtil.parse(\"select * from t_demo where a = 1 for update\").toString());\n        Assertions.assertEquals(\"SELECT * FROM sys_sms_send_record WHERE check_status = 0 ORDER BY submit_time ASC LIMIT 10 FOR UPDATE\",\n            CCJSqlParserUtil.parse(\"select * from sys_sms_send_record where check_status = 0 for update order by submit_time asc limit 10\").toString());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/parser/JsqlParserSimpleSerialTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.FstFactory;\nimport com.baomidou.mybatisplus.extension.parser.cache.FuryFactory;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport org.apache.fury.logging.LoggerFactory;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2023-08-03\n */\nclass JsqlParserSimpleSerialTest {\n    private final static int len = 1000;\n    private final static String sql = \"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\";\n\n    @Test\n    void test() throws JSQLParserException {\n        System.out.println(\"循环次数: \" + len);\n        System.out.println(\"--------------------------------------------------------------------------------\");\n        noSerial();\n        System.out.println(\"--------------------------------------------------------------------------------\");\n        jdkSerial();\n        System.out.println(\"--------------------------------------------------------------------------------\");\n//        fstSerial();\n        furySerial();\n        System.out.println(\"--------------------------------------------------------------------------------\");\n    }\n\n    void noSerial() throws JSQLParserException {\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            CCJSqlParserUtil.parse(sql);\n        }\n        long endTime = System.currentTimeMillis();\n        long e1 = endTime - startTime;\n        System.out.printf(\"普通解析执行耗时: %s 毫秒, 均耗时: %s%n\", e1, (double) e1 / len);\n    }\n\n    void jdkSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = SerializationUtils.serialize(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"jdk serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) SerializationUtils.deserialize(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"jdk deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n\n    void fstSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        FstFactory factory = FstFactory.getDefaultFactory();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = factory.asByteArray(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"fst serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) factory.asObject(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"fst deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n\n    void furySerial() throws JSQLParserException {\n        LoggerFactory.disableLogging();\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        FuryFactory factory = FuryFactory.getFuryFactory();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = factory.serialize(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"fury serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) factory.deserialize(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"fury deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/parser/cache/FstFactoryTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser.cache;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfo;\nimport io.github.classgraph.ScanResult;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2023-08-06\n */\nclass FstFactoryTest {\n\n    @Test\n    void clazz() {\n        List<ClassInfo> list = new ArrayList<>();\n        List<ClassInfo> absList = new ArrayList<>();\n        try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(\"net.sf.jsqlparser\").scan()) {\n            for (ClassInfo classInfo : scanResult.getAllClasses()) {\n                if (!classInfo.isInterface() && classInfo.implementsInterface(Serializable.class)) {\n                    if (classInfo.isAbstract()) {\n                        absList.add(classInfo);\n                        continue;\n                    }\n                    list.add(classInfo);\n                }\n            }\n        }\n        list.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n        System.out.println(\"↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓\");\n        absList.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/MybatisPlusInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-30\n */\nclass MybatisPlusInterceptorTest {\n\n    @Test\n    void setProperties() {\n        Properties properties = new Properties();\n        properties.setProperty(\"@page\", \"com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor\");\n        properties.setProperty(\"page:maxLimit\", \"10\");\n        properties.setProperty(\"page:dbType\", \"h2\");\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.setProperties(properties);\n        List<InnerInterceptor> interceptors = interceptor.getInterceptors();\n\n        assertThat(interceptors).isNotEmpty();\n        assertThat(interceptors.size()).isEqualTo(1);\n\n        InnerInterceptor page = interceptors.getFirst();\n        assertThat(page).isInstanceOf(PaginationInnerInterceptor.class);\n\n        PaginationInnerInterceptor pii = (PaginationInnerInterceptor) page;\n        assertThat(pii.getMaxLimit()).isEqualTo(10);\n        assertThat(pii.getDbType()).isEqualTo(DbType.H2);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/BlockAttackInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-08-18\n */\nclass BlockAttackInnerInterceptorTest {\n\n    private final BlockAttackInnerInterceptor interceptor = new BlockAttackInnerInterceptor();\n\n    @Test\n    void update() {\n        checkEx(\"update user set name = null\", \"null where\");\n        checkEx(\"update user set name = null where 1=1\", \"1=1\");\n        checkEx(\"update user set name = null where 1<>2\", \"1<>2\");\n        checkEx(\"update user set name = null where 1!=2\", \"1!=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n        checkEx(\"update user set name = null where 1=1 and (2=2)\", \"1=1 and (2=2)\");\n        checkEx(\"update user set name = null where (1=1 and 2=2)\", \"(1=1 and 2=2)\");\n        checkEx(\"update user set name = null where (1=1 and (2=3 or 3=3))\", \"(1=1 and (2=3 or 3=3))\");\n\n        checkNotEx(\"update user set name = null where 1=?\", \"1=?\");\n    }\n\n    @Test\n    void delete() {\n        checkEx(\"delete from user\", \"null where\");\n        checkEx(\"delete from user where 1=1\", \"1=1\");\n        checkEx(\"delete from user where 1<>2\", \"1<>2\");\n        checkEx(\"delete from user where 1!=2\", \"1!=2\");\n        checkEx(\"delete from user where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"delete from user where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n    }\n\n    void checkEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNotNull();\n        assertThat(e).as(as).isInstanceOf(MybatisPlusException.class);\n    }\n\n    void checkNotEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNull();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataChangeRecorderInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass DataChangeRecorderInnerInterceptorTest {\n\n    private final DataChangeRecorderInnerInterceptor interceptor = new DataChangeRecorderInnerInterceptor();\n\n    @BeforeEach\n    public void initProperties() {\n        Properties properties = new Properties();\n        properties.put(\"ignoredTableColumns\", \"table_name1.column1,column2; h2user.*; *.column1,COLUMN2\");\n        interceptor.setProperties(properties);\n    }\n    @Test\n    void setProperties() throws Exception {\n        final Object ignoreAllColumns = getFieldValue(interceptor, \"ignoreAllColumns\");\n        Assertions.assertEquals(Set.of(\"COLUMN1\", \"COLUMN2\"), ignoreAllColumns);\n        final Object ignoredTableColumns = getFieldValue(interceptor, \"ignoredTableColumns\");\n        Assertions.assertEquals(Map.of(\"H2USER\", Set.of(\"*\"), \"TABLE_NAME1\", Set.of(\"COLUMN1\", \"COLUMN2\")), ignoredTableColumns);\n    }\n\n    private Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {\n        final Field field = DataChangeRecorderInnerInterceptor.class.getDeclaredField(fieldName);\n        field.setAccessible(true);\n        return  field.get(obj);\n    }\n\n    @Test\n    void processInsert() {\n        final Insert insert = new Insert();\n        insert.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processInsert(insert, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void processUpdate() {\n        final Update update = new Update();\n        update.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processUpdate(update, null, null, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void isDataChangedTest() {\n        var columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        Assertions.assertFalse(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new Object());\n        Assertions.assertTrue(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new BigDecimal(\"0\"));\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertTrue(columnChangeResult.isDataChanged(\"0\"));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(0));\n\n        Assertions.assertFalse(columnChangeResult.isDataChanged(new BigDecimal(\"0\") {}));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(new BigDecimal(\"1\") {}));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * 数据权限拦截器测试\n *\n * @author hubin\n * @since 3.4.1 +\n */\n@Slf4j\npublic class DataPermissionInterceptorTest {\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final Map<String, String> SQL_SEGMENT_MAP = new HashMap<>() {\n        {\n            put(TEST_1, \"username='123' or userId IN (1,2,3)\");\n            put(TEST_2, \"u.state=1 and u.amount > 1000\");\n            put(TEST_3, \"companyId in (1,2,3)\");\n            put(TEST_4, \"username like 'abc%'\");\n            put(TEST_5, \"id=1 and role_id in (select id from sys_role)\");\n        }\n    };\n\n    private static final DataPermissionInterceptor INTERCEPTOR = new DataPermissionInterceptor((where, mappedStatementId) -> {\n        try {\n            String sqlSegment = SQL_SEGMENT_MAP.get(mappedStatementId);\n            Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n            if (null != where) {\n                System.out.println(\"原 where : \" + where);\n                if (mappedStatementId.equals(TEST_4)) {\n                    // 这里测试返回 OR 条件\n                    return new OrExpression(where, sqlSegmentExpression);\n                }\n                return new AndExpression(where, sqlSegmentExpression);\n            }\n            return sqlSegmentExpression;\n        } catch (JSQLParserException e) {\n            log.error(\"解析错误:\", e);\n        }\n        return null;\n    });\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 OR username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(INTERCEPTOR.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * 动态表名内部拦截器测试\n *\n * @author miemie, hcl\n * @since 2020-07-16\n */\nclass DynamicTableNameInnerInterceptorTest {\n\n    /**\n     * 测试 SQL 中的动态表名替换\n     */\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void doIt() {\n        DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();\n        interceptor.setTableNameHandler((sql, tableName) -> tableName + \"_r\");\n\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM /**/t_user_r/* t_user */\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t left join entity_r e on e.id = t.id\", interceptor.changeTable(origin));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameJsqlParserInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author nieqiurong\n */\nclass DynamicTableNameJsqlParserInnerInterceptorTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicTableNameJsqlParserInnerInterceptor.class);\n\n    private static final DynamicTableNameJsqlParserInnerInterceptor interceptor;\n\n    static {\n        interceptor = new DynamicTableNameJsqlParserInnerInterceptor((sql, tableName) -> {\n            LOGGER.info(\"process table : {}\", tableName);\n            if (tableName.endsWith(\"`\") || tableName.endsWith(\"]\")) {\n                char first = tableName.charAt(0);\n                char last = tableName.charAt(tableName.length()-1);\n                return first + SqlParserUtils.removeWrapperSymbol(tableName) + \"_r\" + last;\n            }\n            return tableName + \"_r\";\n        });\n        interceptor.setShouldFallback(true);\n    }\n\n\n    private static final String SQL_SELECT_SUB_QUERY = \"SELECT /*+ materialize*/ strategy_id\"\n        + \"FROM\"\n        + \" ( SELECT  strat.cf_strategy_id \"\n        + \"   FROM strategy strt,\"\n        + \"        doc_sect_ver prodGrp\"\n        + \"  WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"           AND strat.module_type   IN ('sdfdsf','assdf')\"\n        + \")\";\n\n\n    private static final String SQL_SELECT_THREE_JOIN_WITH_ALIASE = \"select c.name, s.name, s.id, r.result\"\n        + \" from colleges c \"\n        + \" join students s\"\n        + \"   on c.id = s.college_id\"\n        + \" join results r\"\n        + \"   on s.id = r.student_id\"\n        + \"where c.id = 3\"\n        + \"  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\";\n\n    private static final String SQL_COMPLEX_ONE = \"INSERT INTO static_product\"\n        + \"  (\"\n        + \"   DISCOUNT_ID,\"\n        + \"    CATEGORY_ID,\"\n        + \"    PRODUCT_ID\"\n        + \"   )\"\n        + \"  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,\"\n        + \"     ALLNDC11.PRODUCT_ID,\"\n        + \"     ALLNDC11.NDC11\"\n        + \"  FROM ITEM ITEM\"\n        + \" INNER JOIN\"\n        + \"   (SELECT NODE.SOURCE_ID NDC11,\"\n        + \"    PR.PRODUCT_ID,\"\n        + \"     BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"        DR_BD_PRODUCT PR,\"\n        + \"       map_edge_ver node\"\n        + \"     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"    AND B.BUNDLE_STATUS         =3\"\n        + \"    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"     AND NODE.EDGE_TYPE          = 1\"\n        + \"      START WITH\"\n        + \"      (\"\n        + \"        NODE.DEST_ID              = PR.PRODUCT_ID\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"      AND B.BUNDLE_STATUS         =3\"\n        + \"      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"      AND NODE.EDGE_TYPE          = 1\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"     )\"\n        + \"       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID\"\n        + \"    AND PRIOR NODE.EDGE_TYPE           = 1\"\n        + \"    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)\"\n        + \"    ) ALLNDC11\"\n        + \"  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)\"\n        + \"  UNION\"\n        + \"   ( SELECT BD1.BUNDLE_DISCOUNT_ID,\"\n        + \"      PR.PRODUCT_ID,\"\n        + \"     ITEM.CAT_MAP_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"     DR_BD_PRODUCT PR,\"\n        + \"    ITEM ITEM\"\n        + \"    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID\"\n        + \"   AND B.BUNDLE_STATUS         =3\"\n        + \"   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"   AND item.cat_map_id         =pr.product_id\"\n        + \"    )\";\n\n    private static final String SQL_MERGE_COMPLEX = \"MERGE INTO  cf_procedure proc USING\"\n        + \" (\"\n        + \" WITH NON_STRATEGY_DETAILS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_strategy_id\"\n        + \"    FROM\"\n        + \"     ( SELECT  strat.cf_strategy_id\"\n        + \"        FROM cf_strategy strat,\"\n        + \"             struct_doc_Sect_ver prodGrp\"\n        + \"        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"                 AND strat.src_mgr_id     = prodGrp.mgr_id\"\n        + \"                 AND strat.src_ver_num    = prodGrp.ver_num\"\n        + \"                 AND strat.module_type   IN ('COMPL','PRCMSTR')\"\n        + \"   )  ),\"\n        + \"   NON_STRATEGY_COMPS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_component_id\"\n        + \"   FROM\"\n        + \"   (\"\n        + \"     SELECT comp.cf_component_id AS cf_component_id\"\n        + \"     FROM   cf_component comp,\"\n        + \"            tier_basis_ver tb\"\n        + \"     WHERE  comp.bucket_src_id   = tb.tier_basis_id\"\n        + \"             AND comp.bucket_src_mgr_id  = tb.mgr_id\"\n        + \"             AND comp.bucket_src_ver_num = tb.ver_num\"\n        + \"             AND comp.module_type       IN ('COMPL','PRCMSTR')\"\n        + \"   )\"\n        + \"   ) ,\"\n        + \" NON_STRAT_PERIODS AS (\"\n        + \"   SELECT /*+ materialize*/ cf_period_id\"\n        + \"   FROM\"\n        + \"         cf_period per,\"\n        + \"         struct_doc_sect_ver prodGrp\"\n        + \"   WHERE  per.src_id            = prodGrp.struct_doc_sect_id\"\n        + \"         AND per.src_mgr_id     = prodGrp.mgr_id\"\n        + \"         AND per.src_ver_num    = prodGrp.ver_num\"\n        + \"         AND per.module_type    IN ('COMPL','PRCMSTR')\"\n        + \"         AND per.pmt_status NOT IN ('TERM','REV')\"\n\n        + \"    SELECT DISTINCT cf_procedure_id\"\n        + \"   FROM\"\n        + \"     (SELECT /*+ LEADING(comp,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  non_strategy_comps comp,\"\n        + \"           cf_procedure proc\"\n        + \"     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND comp.cf_component_id    = proc.cf_component_id\"\n        + \"    UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  cf_procedure proc,\"\n        + \"           non_strategy_details strat\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND strat.cf_strategy_id = proc.cf_strategy_id\"\n        + \"     UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"          proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM cf_procedure proc,\"\n        + \"          non_strat_periods periods\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND periods.CF_PERIOD_ID = proc.period_id\"\n        + \"     )\"\n        + \"      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id)\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET proc.variable_name = 'TierResultSSName';\";\n\n    private static final String SQL_MERGE_COMPLEX_TWO = \" MERGE INTO cf_procedure_ver procVer USING\"\n        + \"   (SELECT cf_procedure_id\"\n        + \"    FROM cf_procedure proc\"\n        + \"    WHERE proc.variable_name                  = 'TierResultSSName'\"\n        + \"   ) proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id )\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET procVer.variable_name = 'TierResultSSName'\"\n        + \"   WHERE procVer.variable_name <> 'TierResultSSName';\";\n\n    @Test\n    public void testSelectOneTable() {\n        var sql = \"SELECT name, age FROM table1 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTables() {\n        var sql = \"SELECT name, age FROM table1,table2\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTables() {\n        var sql = \"SELECT name, age FROM table1,table2,table3 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r, table3_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneTableWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliaseAndNoCondition() {\n        var sql = \"select xx from table1 a,table2 b\";\n        assertEquals(\"SELECT xx FROM table1_r a, table2_r b\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2, table3 t3 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2, table3_r t3 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectWithSubQuery() {\n        assertEquals(\"SELECT /*+ materialize */ strategy_idFROM(SELECT strat.cf_strategy_id FROM strategy_r strt, doc_sect_ver_r prodGrp WHERE strat.src_id = prodGrp.struct_doc_sect_id AND strat.module_type IN ('sdfdsf', 'assdf'))\", interceptor.changeTable(SQL_SELECT_SUB_QUERY));\n    }\n\n    @Test\n    public void testSelectWithOneJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneJoinWithAliase() {\n        var sql = \"SELECT coluname(s) FROM table1 t1 join table2 t2 ON t1.coluname=t2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r t1 JOIN table2_r t2 ON t1.coluname = t2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneLeftJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 left outer join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r LEFT OUTER JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testShouldIgnoreDual() {\n        var sql = \"select * from dual\";\n        assertEquals(\"SELECT * FROM dual_r\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectTwoJoinWithAliase() {\n        assertEquals(\"select c.name, s.name, s.id, r.result from colleges_r c  join students_r s   on c.id = s.college_id join results_r r   on s.id = r.student_idwhere c.id = 3  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\", interceptor.changeTable(SQL_SELECT_THREE_JOIN_WITH_ALIASE));\n    }\n\n    @Test\n    public void testInsertWithValues() {\n        var sql = \"INSERT INTO table_name VALUES (value1,value2,value3,...)\";\n        assertEquals(\"INSERT INTO table_name_r VALUES (value1,value2,value3,...)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertComplex() {\n        assertEquals(\"INSERT INTO static_product_r  (   DISCOUNT_ID,    CATEGORY_ID,    PRODUCT_ID   )  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,     ALLNDC11.PRODUCT_ID,     ALLNDC11.NDC11  FROM ITEM_r ITEM INNER JOIN   (SELECT NODE.SOURCE_ID NDC11,    PR.PRODUCT_ID,     BD1.BUNDLE_DISCOUNT_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,        DR_BD_PRODUCT_r PR,       map_edge_ver_r node     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE      AND B.BUNDLE_ID             =BD1.BUNDLE_ID    AND B.BUNDLE_STATUS         =3    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1     AND NODE.EDGE_TYPE          = 1      START WITH      (        NODE.DEST_ID              = PR.PRODUCT_ID      AND B.BUNDLE_ID             =BD1.BUNDLE_ID      AND B.BUNDLE_STATUS         =3      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1      AND NODE.EDGE_TYPE          = 1      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE     )       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID    AND PRIOR NODE.EDGE_TYPE           = 1    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)    ) ALLNDC11  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)  UNION   ( SELECT BD1.BUNDLE_DISCOUNT_ID,      PR.PRODUCT_ID,     ITEM.CAT_MAP_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,     DR_BD_PRODUCT_r PR,    ITEM_r ITEM    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID   AND B.BUNDLE_STATUS         =3   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID    AND BD1.IS_DYNAMIC_CATEGORY!= 1   AND item.cat_map_id         =pr.product_id    )\", interceptor.changeTable(SQL_COMPLEX_ONE));\n    }\n\n    @Test\n    public void testInsertWithSelect() {\n        var sql = \"INSERT INTO Customers (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers;\";\n        assertEquals(\"INSERT INTO Customers_r (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDelete2() {\n        var sql = \"DELETE FROM validation_task WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf WHERE conf_name IN ('SaleValidation'))\";\n        assertEquals(\"DELETE FROM validation_task_r WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf_r WHERE conf_name IN ('SaleValidation'))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOracleSpecialDelete() {\n        var sql = \"delete table1 where column_name=xyz\";\n        assertEquals(\"DELETE table1_r WHERE column_name = xyz\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter() {\n        var sql = \"ALTER TABLE Persons ADD UNIQUE (P_Id)\";\n        assertEquals(\"ALTER TABLE Persons_r ADD UNIQUE (P_Id)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter2() {\n        var sql = \"ALTER TABLE table_name MODIFY coluname datatype\";\n        assertEquals(\"ALTER TABLE table_name_r MODIFY coluname datatype\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDrop() {\n        var sql = \"DROP table tname;\\n\\r\";\n        assertEquals(\"DROP table tname_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropFunction() {\n        var sql = \"DROP FUNCTION functionName;\";\n        assertEquals(\"DROP FUNCTION functionName\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropProcedure() {\n        var sql = \"drop procedure procedureName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropView() {\n        var sql = \"DROP view viewName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropIndex() {\n        var sql = \"DROP INDEX indexName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUnionAll() {\n        var sql = \"SELECT coluname(s) FROM table1 UNION ALL SELECT coluname(s) FROM table2;\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r UNION ALL SELECT coluname(s) FROM table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMerge() {\n        var sql = \"MERGE INTO employees e  USING hr_records h  ON (e.id = h.emp_id) WHEN MATCHED THEN  UPDATE SET e.address = h.address  WHEN NOT MATCHED THEN    INSERT (id, address) VALUES (h.emp_id, h.address);\";\n        assertEquals(\"MERGE INTO employees_r e USING hr_records_r h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeUsingQuery() {\n        var sql = \"MERGE INTO employees e USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h  ON (e.id = h.emp_id)  WHEN MATCHED THEN  UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\";\n        assertEquals(\"MERGE INTO employees_r e USING (SELECT * FROM hr_records_r WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeComplexQuery() {\n        assertEquals(\"MERGE INTO  cf_procedure_r proc USING ( WITH NON_STRATEGY_DETAILS AS   (   SELECT /*+ materialize*/ cf_strategy_id    FROM     ( SELECT  strat.cf_strategy_id        FROM cf_strategy_r strat,             struct_doc_Sect_ver_r prodGrp        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id                 AND strat.src_mgr_id     = prodGrp.mgr_id                 AND strat.src_ver_num    = prodGrp.ver_num                 AND strat.module_type   IN ('COMPL','PRCMSTR')   )  ),   NON_STRATEGY_COMPS AS   (   SELECT /*+ materialize*/ cf_component_id   FROM   (     SELECT comp.cf_component_id AS cf_component_id     FROM   cf_component_r comp,            tier_basis_ver_r tb     WHERE  comp.bucket_src_id   = tb.tier_basis_id             AND comp.bucket_src_mgr_id  = tb.mgr_id             AND comp.bucket_src_ver_num = tb.ver_num             AND comp.module_type       IN ('COMPL','PRCMSTR')   )   ) , NON_STRAT_PERIODS AS (   SELECT /*+ materialize*/ cf_period_id   FROM         cf_period_r per,         struct_doc_sect_ver_r prodGrp   WHERE  per.src_id            = prodGrp.struct_doc_sect_id         AND per.src_mgr_id     = prodGrp.mgr_id         AND per.src_ver_num    = prodGrp.ver_num         AND per.module_type    IN ('COMPL','PRCMSTR')         AND per.pmt_status NOT IN ('TERM','REV')    SELECT DISTINCT cf_procedure_id   FROM     (SELECT /*+ LEADING(comp,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  non_strategy_comps_r comp,           cf_procedure_r proc     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'           AND comp.cf_component_id    = proc.cf_component_id    UNION ALL     SELECT  /*+ LEADING(strat,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  cf_procedure_r proc,           non_strategy_details_r strat     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND strat.cf_strategy_id = proc.cf_strategy_id     UNION ALL     SELECT  /*+ LEADING(strat,proc)*/          proc.cf_procedure_id AS cf_procedure_id     FROM cf_procedure_r proc,          non_strat_periods_r periods     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND periods.CF_PERIOD_ID = proc.period_id     )      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id) WHEN MATCHED THEN   UPDATE SET proc.variable_name = 'TierResultSSName';\", interceptor.changeTable(SQL_MERGE_COMPLEX));\n    }\n\n    @Test\n    public void testMergeComplexQuery2() {\n        assertEquals(\"MERGE INTO cf_procedure_ver_r procVer USING (SELECT cf_procedure_id FROM cf_procedure_r proc WHERE proc.variable_name = 'TierResultSSName') proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id) WHEN MATCHED THEN UPDATE SET procVer.variable_name = 'TierResultSSName' WHERE procVer.variable_name <> 'TierResultSSName'\", interceptor.changeTable(SQL_MERGE_COMPLEX_TWO));\n    }\n\n    @Test\n    public void testCreateTable2() {\n        var sql = \"CREATE TABLE Persons(PersonID int,LastName varchar(255),FirstName varchar(255),Address varchar(255),City varchar(255));\";\n        assertEquals(\"CREATE TABLE Persons_r (PersonID int, LastName varchar (255), FirstName varchar (255), Address varchar (255), City varchar (255))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateGlobalTable() {\n        var sql = \"CREATE GLOBAL TEMPORARY TABLE excl_cust (gen_name VARCHAR2(100),run_date TIMESTAMP(3), item_root_uuid  VARCHAR2(22), owner_member_id  NUMBER(20)) ON COMMIT DELETE ROWS\";\n        assertEquals(\"CREATE GLOBAL TEMPORARY TABLE excl_cust_r (gen_name VARCHAR2 (100), run_date TIMESTAMP (3), item_root_uuid VARCHAR2 (22), owner_member_id NUMBER (20)) ON COMMIT DELETE ROWS\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateIndex() {\n        var sql = \"CREATE INDEX temp_name_idx ON table1(name) NOLOGGING PARALLEL (DEGREE 8);\";\n        assertEquals(\"CREATE INDEX temp_name_idx ON table1_r (name) NOLOGGING PARALLEL (DEGREE8)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView() {\n        var sql = \"CREATE VIEW dept AS SELECT * FROM dept;\";\n        assertEquals(\"CREATE VIEW dept AS SELECT * FROM dept_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView2() {\n        var sql = \"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp, dept WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno;\";\n        assertEquals(\"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp_r, dept_r WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateType() {\n        var sql = \"CREATE OR REPLACE TYPE TYPE_NAME IS TABLE OF VARCHAR2(100)\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTable() {\n        var sql = \"UPDATE tableName SET column1 = expression1, column2 = expression2\";\n        assertEquals(\"UPDATE tableName_r SET column1 = expression1, column2 = expression2\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery() {\n        var sql = \"UPDATE table1 SET table1.value = (SELECT table2.CODE FROM table2 WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE='blah' AND EXISTS (SELECT table2.CODE  FROM table2    WHERE table1.value = table2.DESC);\";\n        assertEquals(\"UPDATE table1_r SET table1.value = (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE = 'blah' AND EXISTS (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery2() {\n        var sql = \"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1 INNER JOIN table2 ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\";\n        assertEquals(\"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1_r INNER JOIN table2_r ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQueryWithOracleHint() {\n        var sql = \"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\";\n        assertEquals(\"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible_r ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testTruncateTable() {\n        var sql = \"truncate table eligible_item\";\n        assertEquals(\"TRUNCATE TABLE eligible_item_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithComment() {\n        var sql = \"select * from foo -- this is a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentContainingKeyword() {\n        var sql = \"select * from foo -- what happens if I say update in a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentEndingWithKeyword() {\n        var sql = \"select * from foo -- what happens if I end a comment with an update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsAndNewlines() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n\\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectForUpdate() {\n        //TODO 暂时解决不能使用的问题,当碰到for update nowait这样的,后面的 nowait 会被当做成表但也不是很影响苗老板的动态表过滤.\n        var sql = \"select * from mp where id = 1 for update\";\n        assertEquals(\"SELECT * FROM mp_r WHERE id = 1 FOR UPDATE\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOnDuplicateKeyUpdate () {\n        var sql = \"INSERT INTO cf_procedure (_id,password) VALUES ('1','password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword';\";\n        assertEquals(\"INSERT INTO cf_procedure_r (_id, password) VALUES ('1', 'password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateIgnore() {\n        var sql = \"update ignore student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n\n        sql = \"UPDATE IGNORE student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertIgnore() {\n        var sql = \"INSERT IGNORE INTO student (userid,username) VALUES (2,'swan'),(4,'bear') ;\";\n        assertEquals(\"INSERT IGNORE INTO student_r (userid, username) VALUES (2, 'swan'), (4, 'bear')\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateUniqueIndex() {\n        var sql = \"CREATE UNIQUE INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE UNIQUE INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1_r ADD UNIQUE INDEX `a` (`a`)\";\n        assertEquals(\"ALTER TABLE table1_r_r ADD UNIQUE INDEX `a` (`a`)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateFullTextIndex(){\n        var sql = \"CREATE FULLTEXT INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE FULLTEXT INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1 ADD FULLTEXT INDEX `a`(`a`)\";\n        assertEquals(\"ALTER TABLE table1_r ADD FULLTEXT INDEX `a` (`a`)\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void test() {\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t LEFT JOIN entity_r e ON e.id = t.id\", interceptor.changeTable(origin));\n    }\n\n    @Test\n    void testCreateTable() {\n        var sql = \"\"\"\n            CREATE TABLE `tag`  (\n              `id` int(11) NOT NULL AUTO_INCREMENT,\n              `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字',\n              `type` int(11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别',\n              PRIMARY KEY (`id`) USING BTREE\n            ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic;\n            \"\"\";\n        assertEquals(\"CREATE TABLE `tag_r` (`id` int (11) NOT NULL AUTO_INCREMENT, `name` varchar (50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字', `type` int (11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别', PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateTableIfNotExists() {\n        var sql = \"\"\"\n            CREATE TABLE IF NOT EXISTS `user_info` (\n                `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n                `username` VARCHAR(50) NOT NULL UNIQUE,\n                `email` VARCHAR(100) NOT NULL UNIQUE,\n                `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n            \"\"\";\n        assertEquals(\"CREATE TABLE IF NOT EXISTS `user_info_r` (`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR (50) NOT NULL UNIQUE, `email` VARCHAR (100) NOT NULL UNIQUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDropTableIfExists() {\n        var sql = \"DROP TABLE IF EXISTS `tag`\";\n        assertEquals(\"DROP TABLE IF EXISTS `tag_r`\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testIssues6730() {\n        // https://github.com/baomidou/mybatis-plus/issues/6730\n        var sql = \"select * from user order by top_bottom_sort desc, 0- EXTRACT(EPOCH FROM req_delivery_time) desc\";\n        assertEquals(\"SELECT * FROM user_r ORDER BY top_bottom_sort DESC, 0 - EXTRACT(EPOCH FROM req_delivery_time) DESC\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectJoin() {\n        var sql = \"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\";\n        assertEquals(\"SELECT * FROM entity_r e JOIN entity1_r e1 ON e1.id = e.id WHERE e.id = ? OR e.name = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectWithAs() {\n        var sql = \"with with_as_A as (select * from entity) select * from with_as_A\";\n        assertEquals(\"WITH with_as_A AS (SELECT * FROM entity_r) SELECT * FROM with_as_A_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDuplicateKeyUpdate() {\n        var sql = \"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\";\n        assertEquals(\"INSERT INTO entity_r (name, age) VALUES ('秋秋', 18), ('秋秋', '22') ON DUPLICATE KEY UPDATE age = 18\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDelete() {\n        var sql = \"delete from entity where id = ?\";\n        assertEquals(\"DELETE FROM entity_r WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testUpdate() {\n        var sql = \"update entity set name = ? where id = ?\";\n        assertEquals(\"UPDATE entity_r SET name = ? WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testPartition() {\n        // 这种jsql解析不了\n        var sql = \"\"\"\n            -- 查询2023年Q2分区数据\n            SELECT\\s\n                region,\n                SUM(gross_profit) AS 区域总利润,\n                AVG(order_value) AS 平均订单金额\n            FROM\\s\n                sales_data\n            PARTITION BY\\s\n                (TO_DATE(order_date, 'YYYY-MM-DD'))\n            INTERVAL MONTHLY\n            FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\n            GROUP BY\\s\n                region;\n            \"\"\";\n        assertEquals(\"-- 查询2023年Q2分区数据\\n\" +\n            \"SELECT \\n\" +\n            \"    region,\\n\" +\n            \"    SUM(gross_profit) AS 区域总利润,\\n\" +\n            \"    AVG(order_value) AS 平均订单金额\\n\" +\n            \"FROM \\n\" +\n            \"    sales_data_r\\n\" +\n            \"PARTITION BY \\n\" +\n            \"    (TO_DATE(order_date, 'YYYY-MM-DD'))\\n\" +\n            \"INTERVAL MONTHLY\\n\" +\n            \"FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\\n\" +\n            \"GROUP BY \\n\" +\n            \"    region;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test2() {\n        var sql = \"\"\"\n            SELECT\\s\n                COUNT(*) AS 订单总数,\n                SUM(o.order_total) AS 总销售额,\n                SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数\n            FROM\\s\n                orders o\n            JOIN\\s\n                customers c ON o.customer_id = c.customer_id\n            JOIN\\s\n                order_items oi ON o.order_id = oi.order_id\n            WHERE\\s\n                c.region = 'North America'\n                AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30'\n            GROUP BY\\s\n                o.customer_id\n            HAVING\\s\n                COUNT(*) > 10;\n            ORDER BY\\s\n                total_sales DESC;\n            \"\"\";\n        assertEquals(\"SELECT COUNT(*) AS 订单总数, SUM(o.order_total) AS 总销售额, SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数 FROM orders_r o JOIN customers_r c ON o.customer_id = c.customer_id JOIN order_items_r oi ON o.order_id = oi.order_id WHERE c.region = 'North America' AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30' GROUP BY o.customer_id HAVING COUNT(*) > 10\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test3() {\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            DELIMITER $$\n            DECLARE\\s\n                cur CURSOR FOR\\s\n                    SELECT employee_id FROM employees WHERE salary < 50000;\n                emp_id INT;\n            BEGIN\n                OPEN cur;\n                WHILE TRUE DO\n                    FETCH cur INTO emp_id;\n                    IF cur_rowcount = 0 THEN\n                        LEAVE;\n                    END IF;\n                   \\s\n                    UPDATE employees\\s\n                    SET salary = salary * 1.1\\s\n                    WHERE employee_id = emp_id;\n                   \\s\n                    INSERT INTO audit_log (employee_id, old_salary, new_salary)\n                    VALUES (emp_id, salary_before_update, salary_after_update);\n                END WHILE;\n                CLOSE cur;\n            END\n            $$\n            DELIMITER ;\n            \"\"\";\n        assertEquals(\"DELIMITER $$\\n\" +\n            \"DECLARE \\n\" +\n            \"    cur CURSOR FOR \\n\" +\n            \"        SELECT employee_id FROM employees_r WHERE salary < 50000;\\n\" +\n            \"    emp_id INT;\\n\" +\n            \"BEGIN\\n\" +\n            \"    OPEN cur;\\n\" +\n            \"    WHILE TRUE DO\\n\" +\n            \"        FETCH cur INTO emp_id_r;\\n\" +\n            \"        IF cur_rowcount = 0 THEN\\n\" +\n            \"            LEAVE;\\n\" +\n            \"        END IF;\\n\" +\n            \"        \\n\" +\n            \"        UPDATE employees_r \\n\" +\n            \"        SET salary = salary * 1.1 \\n\" +\n            \"        WHERE employee_id = emp_id;\\n\" +\n            \"        \\n\" +\n            \"        INSERT INTO audit_log_r (employee_id, old_salary, new_salary)\\n\" +\n            \"        VALUES (emp_id, salary_before_update, salary_after_update);\\n\" +\n            \"    END WHILE;\\n\" +\n            \"    CLOSE cur;\\n\" +\n            \"END\\n\" +\n            \"$$\\n\" +\n            \"DELIMITER ;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test4() {\n        var sql = \"\"\"\n            SELECT *\n            FROM employees e\n            JOIN departments d ON e.department_id = d.department_id\n            WHERE\\s\n                e.last_name LIKE CONCAT('%', :lastName, '%')\n                AND (\n                    d.department_name IN (:departmentList)\n                    OR :departmentList IS NULL\n                )\n                AND (\n                    e.hire_date >= :startDate\n                    OR :startDate IS NULL\n                )\n            ORDER BY\\s\n                e.employee_id\n            \"\"\";\n        assertEquals(\"SELECT * FROM employees_r e JOIN departments_r d ON e.department_id = d.department_id WHERE e.last_name LIKE CONCAT('%', :lastName, '%') AND (d.department_name IN (:departmentList) OR :departmentList IS NULL) AND (e.hire_date >= :startDate OR :startDate IS NULL) ORDER BY e.employee_id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test5() {\n        var sql  = \"\"\"\n            SELECT\\s\n                product_id,\n                product_name,\n                stock_quantity,\n                (SELECT\\s\n                    SUM(ordered_qty)\\s\n                 FROM\\s\n                    purchase_orders po\\s\n                 WHERE\\s\n                    po.product_id = products.product_id\\s\n                    AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume\n            FROM\\s\n                products\n            WHERE\\s\n                stock_quantity < (\n                    SELECT\\s\n                        AVG(recommended_stock)\\s\n                    FROM\\s\n                        product_settings\\s\n                    WHERE\\s\n                        product_id = products.product_id\n                )\n                AND recent_order_volume > 500\n            \"\"\";\n        assertEquals(\"SELECT product_id, product_name, stock_quantity, (SELECT SUM(ordered_qty) FROM purchase_orders_r po WHERE po.product_id = products.product_id AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume FROM products_r WHERE stock_quantity < (SELECT AVG(recommended_stock) FROM product_settings_r WHERE product_id = products.product_id) AND recent_order_volume > 500\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test6() {\n        var sql = \"\"\"\n            WITH user_activity AS (\n                SELECT\\s\n                    user_id,\n                    event_type,\n                    event_time,\n                    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq\n                FROM\\s\n                    user_events\n            )\n            SELECT\\s\n                user_id,\n                event_type,\n                event_time,\n                LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time\n            FROM\\s\n                user_activity\n            WHERE\\s\n                activity_seq = 5\n            \"\"\";\n        assertEquals(\"WITH user_activity AS (SELECT user_id, event_type, event_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq FROM user_events_r) SELECT user_id, event_type, event_time, LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time FROM user_activity_r WHERE activity_seq = 5\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test7() {\n        var sql = \"select * from db1.test where a = ?\";\n        assertEquals(\"SELECT * FROM db1.test_r WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test8() {\n        // 这种jsql解析不了的\n        var sql = \"SELECT * FROM [HR].[dbo].[Employee_Salary_2023];\";\n        assertEquals(\"SELECT * FROM [HR].[dbo].[Employee_Salary_2023_r];\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test9(){\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            SELECT * FROM [SalesDB].[dbo].[Orders]\n            JOIN [MarketingDB].[dbo].[Customers]\\s\n            ON Orders.CustomerID = Customers.CustomerID;\n            \"\"\";\n        assertEquals(\"SELECT * FROM [SalesDB].[dbo].[Orders_r]\\n\" +\n            \"JOIN [MarketingDB].[dbo].[Customers_r] \\n\" +\n            \"ON Orders.CustomerID = Customers.CustomerID;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test10() {\n        var sql = \"\"\"\n            SELECT * FROM ecommerce_orders\\s\n            PARTITION (p2022, p2023)\n            WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31';\n            \"\"\";\n        assertEquals(\"SELECT * FROM ecommerce_orders_r PARTITION(p2022, p2023) WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test11() {\n        var sql = \"\"\"\n            SELECT order_id, customer_id,amount,\n                  RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank\n                FROM orders;\n            \"\"\";\n        assertEquals(\"SELECT order_id, customer_id, amount, RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank FROM orders_r\", interceptor.changeTable(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/IllegalSQLInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.h2.jdbcx.JdbcDataSource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * @author miemie\n * @since 2022-04-11\n */\nclass IllegalSQLInnerInterceptorTest {\n\n    private final IllegalSQLInnerInterceptor interceptor = new IllegalSQLInnerInterceptor();\n\n    private static DataSource dataSource;\n\n    @BeforeAll\n    public static void beforeAll() throws SQLException {\n        var jdbcDataSource = new JdbcDataSource();\n        jdbcDataSource.setURL(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        jdbcDataSource.setPassword(\"\");\n        jdbcDataSource.setUser(\"sa\");\n        dataSource = jdbcDataSource;\n        Connection connection = jdbcDataSource.getConnection();\n        var sql = \"\"\"\n            CREATE TABLE T_DEMO (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index1` (`a`,`b`)\n            );\n            CREATE TABLE T_TEST (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index2` (`a`,`b`)\n            );\n            \"\"\";\n        SqlRunner sqlRunner = new SqlRunner(connection);\n        sqlRunner.run(sql);\n    }\n\n    @Test\n    void test() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT COUNT(*) AS total FROM t_user WHERE (client_id = ?)\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete from t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age != 1\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where (age = 1 or name = 'test')\", null));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where  b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.`T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `test` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (c = 3 OR b = 2)\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3 OR b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 3 AND (c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) or b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) AND b = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 AND b = 2)\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` a INNER JOIN T_TEST b ON a.a = b.a where a.a = 3 AND (b.c = 3 OR b.b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a != (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        //TODO 低版本这里的抛异常了.看着应该不用抛出\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n    }\n    @Test\n    void testCount() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a) c\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO`) a \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1)) a \", dataSource.getConnection()));\n    }\n\n    @Test\n    void testCatalogAndSchemaName() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from TEST.PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        // 非同一模式,读不到索引的情况\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from PUBLIC.DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/MultiDataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport com.google.common.collect.HashBasedTable;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.schema.Table;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL多表场景的数据权限拦截器测试\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic class MultiDataPermissionInterceptorTest {\n    private static final Logger logger = LoggerFactory.getLogger(MultiDataPermissionInterceptorTest.class);\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final com.google.common.collect.Table<String, String, String> sqlSegmentMap;\n    private static final DataPermissionInterceptor interceptor;\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n    private static final String TEST_6 = \"com.baomidou.roleMapper.selectUserInfo\";\n    private static final String TEST_7 = \"com.baomidou.roleMapper.summarySum\";\n    private static final String TEST_8_1 = \"com.baomidou.CustomMapper.selectByOnlyMyData\";\n    private static final String TEST_8_2 = \"com.baomidou.CustomMapper.selectByOnlyOrgData\";\n    private static final String TEST_8_3 = \"com.baomidou.CustomMapper.selectByOnlyDeptData\";\n    private static final String TEST_8_4 = \"com.baomidou.CustomMapper.selectByMyDataOrDeptData\";\n    private static final String TEST_8_5 = \"com.baomidou.CustomMapper.selectByMyData\";\n\n    static {\n        sqlSegmentMap = HashBasedTable.create();\n        sqlSegmentMap.put(TEST_1, \"sys_user\", \"username='123' or userId IN (1,2,3)\");\n        sqlSegmentMap.put(TEST_2, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_3, \"sys_role\", \"companyId in (1,2,3)\");\n        sqlSegmentMap.put(TEST_4, \"sys_role\", \"username like 'abc%'\");\n        sqlSegmentMap.put(TEST_5, \"sys_role\", \"id=1 and role_id in (select id from sys_role)\");\n        sqlSegmentMap.put(TEST_6, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_6, \"sys_user_role\", \"r.role_id=3 AND r.role_id IN (7,9,11)\");\n        sqlSegmentMap.put(TEST_7, \"`fund`\", \"a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111\");\n        sqlSegmentMap.put(TEST_7, \"`fund_month`\", \"b.fund_id = 2 AND b.month <= '2022-05'\");\n        sqlSegmentMap.put(TEST_8_1, \"fund\", \"user_id=1\");\n        sqlSegmentMap.put(TEST_8_2, \"fund\", \"org_id=1\");\n        sqlSegmentMap.put(TEST_8_3, \"fund\", \"dept_id=1\");\n        sqlSegmentMap.put(TEST_8_4, \"fund\", \"user_id=1 or dept_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table1\", \"u.user_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table2\", \"u.dept_id=1\");\n        interceptor = new DataPermissionInterceptor(new MultiDataPermissionHandler() {\n\n            @Override\n            public Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId) {\n                try {\n                    String sqlSegment = sqlSegmentMap.get(mappedStatementId, table.getName());\n                    if (sqlSegment == null) {\n                        logger.info(\"{} {} AS {} : NOT FOUND\", mappedStatementId, table.getName(), table.getAlias());\n                        return null;\n                    }\n                    if (table.getAlias() != null) {\n                        // 替换表别名\n                        sqlSegment = sqlSegment.replaceAll(\"u\\\\.\", table.getAlias().getName() + StringPool.DOT);\n                    }\n                    Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n                    logger.info(\"{} {} AS {} : {}\", mappedStatementId, table.getName(), table.getAlias(), sqlSegmentExpression.toString());\n                    return sqlSegmentExpression;\n                } catch (JSQLParserException e) {\n                    logger.error(\"解析错误:\", e);\n                }\n                return null;\n            }\n        });\n    }\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    @Test\n    void test6() {\n        // 显式指定 JOIN 类型时 JOIN 右侧表才能进行拼接条件\n        assertSql(TEST_6, \"select u.username from sys_user u LEFT join sys_user_role r on u.id=r.user_id\",\n            \"SELECT u.username FROM sys_user u LEFT JOIN sys_user_role r ON u.id = r.user_id AND r.role_id = 3 AND r.role_id IN (7, 9, 11) WHERE u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test7() {\n        assertSql(TEST_7, \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = true) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\",\n            \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = true AND b.fund_id = 2 AND b.month <= '2022-05' WHERE a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\");\n    }\n\n    @Test\n    void test8() {\n        assertSql(TEST_8_1, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1\");\n        assertSql(TEST_8_2, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND org_id = 1\");\n        assertSql(TEST_8_3, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND dept_id = 1\");\n        assertSql(TEST_8_4, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1 OR dept_id = 1\");\n        // 修改之前旧版的多表数据权限对这个SQL的表现形式：\n        // 输入 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\"\n        // 输出 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid) SELECT * FROM temp\"\n        // 修改之后的多表数据权限对这个SQL的表现形式\n        assertSql(TEST_8_5, \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\",\n            \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid AND t2.dept_id = 1 WHERE t1.user_id = 1) SELECT * FROM temp\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(interceptor.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/PaginationInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass PaginationInnerInterceptorTest {\n\n    private final PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();\n\n    @Test\n    void optimizeCount() {\n        /* 能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select distinct id from table order by id\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n\n        assertsCountSql(\"select distinct id from table\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n    }\n\n    @Test\n    void notOptimizeCount() {\n        /* 不能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? where u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? WHERE u.xx = ?\");\n\n        /* join 表与 where 条件大小写不同的情况 */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id where R.NAME = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE R.NAME = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\");\n    }\n\n    @Test\n    void optimizeCountOrderBy() {\n        /* order by 里不带参数,去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY name\",\n            \"SELECT COUNT(*) AS total FROM comment\");\n\n        /* order by 里带参数,不去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\",\n            \"SELECT COUNT(*) AS total FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\");\n    }\n\n    @Test\n    void withAsCount() {\n        assertsCountSql(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT COUNT(*) AS total FROM A\");\n    }\n\n    @Test\n    void withAsOrderBy() {\n        assertsConcatOrderBy(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT * FROM A ORDER BY column ASC\",\n            OrderItem.asc(\"column\"));\n    }\n\n    @Test\n    void groupByCount() {\n        assertsCountSql(\"SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)\",\n            \"SELECT COUNT(*) FROM (SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)) TOTAL\");\n    }\n\n    @Test\n    void leftJoinSelectCount() {\n        assertsCountSql(\"select r.id, r.name, r.phone,rlr.total_top_up from reseller r \" +\n                \"left join (select ral.reseller_id, sum(ral.top_up_money) as total_top_up, sum(ral.acquire_money) as total_acquire \" +\n                \"from reseller_acquire_log ral \" +\n                \"group by ral.reseller_id) rlr on r.id = rlr.reseller_id \" +\n                \"order by r.created_at desc\",\n            \"SELECT COUNT(*) AS total FROM reseller r\");\n\n        // 不优化\n        assertsCountSql(\"SELECT f.ca, f.cb FROM table_a f LEFT JOIN \" +\n                \"(SELECT ca FROM table_b WHERE cc = ?) rf on rf.ca = f.ca\",\n            \"SELECT COUNT(*) AS total FROM table_a f LEFT JOIN (SELECT ca FROM table_b WHERE cc = ?) rf ON rf.ca = f.ca\");\n\n        assertsCountSql(\"select * from order_info left join (select count(1) from order_info where create_time between ? and ?) tt on 1=1 WHERE equipment_id=?\",\n            \"SELECT COUNT(*) AS total FROM order_info LEFT JOIN (SELECT count(1) FROM order_info WHERE create_time BETWEEN ? AND ?) tt ON 1 = 1 WHERE equipment_id = ?\");\n    }\n\n    void assertsCountSql(String sql, String targetSql) {\n        assertThat(interceptor.autoCountSql(new Page<>(), sql)).isEqualTo(targetSql);\n    }\n\n    void assertsConcatOrderBy(String sql, String targetSql, OrderItem... orderItems) {\n        assertThat(interceptor.concatOrderBy(sql, Arrays.asList(orderItems))).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/TenantLineInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-07-30\n */\nclass TenantLineInnerInterceptorTest {\n\n    private static final Map<String, String> FIRS_RESULT_TMAP = new HashMap<>();\n\n    private static final Map<String, String> LAST_RESULT_TMAP = new HashMap<>();\n\n    static {\n        firstResultMap();\n        lastResultMap();\n    }\n\n    static void firstResultMap() {\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        FIRS_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE tenant_id = 1 AND id = ?\");\n\n        // set subSelect\n        FIRS_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?) WHERE e.tenant_id = 1 AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        /* not */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND NOT (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE u.tenant_id = 1 AND NOT (u.id = ? OR u.name = ?)\");\n\n        /* in */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在最前\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n        // 在最后\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在中间\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* inner not = */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.tenant_id = 1 AND e.id = ? LIMIT 1)\");\n\n        /* NOT EXISTS */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* >= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <> */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t2.tenant_id = 1 AND t1.col1 = t2.col1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.tenant_id = 1 AND e2.id = 1) = 1, e2.type, e1.type) AS type FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?\");\n\n        // left join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e.tenant_id = 1\");\n\n        // right join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e RIGHT JOIN entity1 e1 ON e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id INNER JOIN entity2 e2 ON e.tenant_id = 1 AND e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) WHERE e1.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) RIGHT JOIN entity3 e3 ON e1.tenant_id = 1 AND e1.id = e3.id WHERE e3.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) ON e2.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e1.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e.tenant_id = 1 AND e.id = e2.id WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN with_as_A e2 ON e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        // inner join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE e.id = ? OR e.name = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN with_as_1 w1 ON e.tenant_id = 1 AND w1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b WHERE a.tenant_id = 1 AND a.id = b.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        // 沙雕的括号写法\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        FIRS_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n    static void lastResultMap() {\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        LAST_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE id = ? AND tenant_id = 1\");\n\n        // set subSelect\n        LAST_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1) \" +\n                \"WHERE e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        /* not */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE NOT (u.id = ? OR u.name = ?) AND u.tenant_id = 1\");\n\n        /* in */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在最前\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n        // 在最后\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在中间\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* inner not = */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1)) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? AND e.tenant_id = 1 LIMIT 1)\");\n\n        /* NOT EXISTS */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* >= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* <= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* <> */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1)\");\n\n        LAST_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t1.col1 = t2.col1 AND t2.tenant_id = 1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1 AND e2.tenant_id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1\");\n\n        // left join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        // right join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"WHERE e1.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 \" +\n                \"WHERE e3.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id AND e2.tenant_id = 1 \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        // inner join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE e.id = ? OR e.name = ?\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN with_as_1 w1 ON w1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 \" +\n                \"WHERE e.id = e1.id AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id AND a.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        // 沙雕的括号写法\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        LAST_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n\n    private final TenantLineInnerInterceptor interceptor = new TenantLineInnerInterceptor(new TenantLineHandler() {\n        private boolean ignoreFirst;// 需要执行 getTenantId 前必须先执行 ignoreTable\n\n        @Override\n        public Expression getTenantId() {\n            assertThat(ignoreFirst).isEqualTo(true);\n            ignoreFirst = false;\n            return new LongValue(1);\n        }\n\n        @Override\n        public boolean ignoreTable(String tableName) {\n            ignoreFirst = true;\n            return tableName.startsWith(\"with_as\");\n        }\n    });\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    public void test(ExpressionAppendMode appendMode) {\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == appendMode ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertMap.forEach((k, v) -> assertSql(k, appendMode));\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void insert(ExpressionAppendMode appendMode) {\n        // plain\n        assertSql(\"insert into entity (id) values (?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?)\", appendMode);\n        // batch\n        assertSql(\"insert into entity (id) values (?),(?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?),(?,?)\", appendMode);\n        // 无 insert的列\n        assertSql(\"insert into entity value (?,?)\", appendMode);\n        // 自己加了insert的列\n        assertSql(\"insert into entity (id,name,tenant_id) value (?,?,?)\", appendMode);\n        // insert into select\n        assertSql(\"insert into entity (id,name) select id,name from entity2\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from entity2 e2\", appendMode);\n        assertSql(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void delete(ExpressionAppendMode appendMode) {\n        assertSql(\"delete from entity where id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void update(ExpressionAppendMode appendMode) {\n        assertSql(\"update entity set name = ? where id = ?\", appendMode);\n        // set subSelect\n        assertSql(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\", appendMode);\n\n        assertSql(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\", appendMode);\n\n        assertSql(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingle(ExpressionAppendMode appendMode) {\n        // 单表\n        assertSql(\"select * from entity where id = ?\", appendMode);\n\n        assertSql(\"select * from entity where id = ? or name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", appendMode);\n\n        /* not */\n        assertSql(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectIn(ExpressionAppendMode appendMode) {\n        /* in */\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在最前\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n        // 在最后\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在中间\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectEq(ExpressionAppendMode appendMode) {\n        /* = */\n        assertSql(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectInnerNotEq(ExpressionAppendMode appendMode) {\n        /* inner not = */\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectExists(ExpressionAppendMode appendMode) {\n        /* EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n\n        assertSql(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", appendMode);\n\n        /* NOT EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWhereSubSelect(ExpressionAppendMode appendMode) {\n        /* >= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <> */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectFromSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodySubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodyFuncSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n            \"FROM entity e1 WHERE e1.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoin(ExpressionAppendMode appendMode) {\n        // left join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectRightJoin(ExpressionAppendMode appendMode) {\n        // right join\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectMixJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n    }\n\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectJoinSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select * from (select * from entity e) e1 \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n\n        assertSql(\"select * from entity1 e1 \" +\n            \"left join (select * from entity2 e2) e22 \" +\n            \"on e1.id = e22.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n            \"right join entity3 e3 on e1.id = e3.id\", appendMode);\n\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoinMultipleTrailingOn(ExpressionAppendMode appendMode) {\n        // 多个 on 尾缀的\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectInnerJoin(ExpressionAppendMode appendMode) {\n        // inner join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // ignore table\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join with_as_1 w1 on w1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity e,entity1 e1 \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        assertSql(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        // SubJoin with 隐式内连接\n        assertSql(\"SELECT * FROM (entity e,entity1 e1) \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        assertSql(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        assertSql(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        // 沙雕的括号写法\n        assertSql(\"SELECT * FROM (((entity e,entity1 e1))) WHERE e.id = e1.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingleJoin(ExpressionAppendMode appendMode) {\n        // join\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWithAs(ExpressionAppendMode appendMode) {\n        assertSql(\"with with_as_A as (select * from entity) select * from with_as_A\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void testDuplicateKeyUpdate(ExpressionAppendMode appendMode) {\n        assertSql(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\", appendMode);\n    }\n\n    void assertSql(String sql, ExpressionAppendMode appendMode) {\n        interceptor.setExpressionAppendMode(appendMode);\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == interceptor.getExpressionAppendMode() ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertThat(interceptor.parserSingle(sql, null)).isEqualTo(assertMap.get(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser/src/test/java/com/baomidou/mybatisplus/test/pagination/SelectBodyToPlainSelectTest.java",
    "content": "package com.baomidou.mybatisplus.test.pagination;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SelectBody强转PlainSelect不支持sql里面最外层带union\n * 用SetOperationList处理sql带union的语句\n */\nclass SelectBodyToPlainSelectTest {\n\n    private static final List<OrderItem> ITEMS = new ArrayList<>();\n\n    static {\n        ITEMS.add(OrderItem.asc(\"column\"));\n    }\n\n    /**\n     * 报错的测试\n     */\n    @Test\n    void testSelectBodyToPlainSelectThrowException() {\n        Select selectStatement = null;\n        try {\n            String originalUnionSql = \"select * from test union select * from test\";\n            selectStatement = (Select) CCJSqlParserUtil.parse(originalUnionSql);\n        } catch (JSQLParserException e) {\n            e.printStackTrace();\n        }\n        assert selectStatement != null;\n        Select finalSelectStatement = selectStatement;\n        Assertions.assertThrows(ClassCastException.class, () -> {\n            PlainSelect plainSelect = (PlainSelect) finalSelectStatement.getSelectBody();\n        });\n    }\n\n    @BeforeEach\n    void setup() {\n        List<OrderItem> orderItems = new ArrayList<>();\n        OrderItem order = new OrderItem();\n        order.setAsc(true);\n        order.setColumn(\"column\");\n        orderItems.add(order);\n        OrderItem orderEmptyColumn = new OrderItem();\n        orderEmptyColumn.setAsc(false);\n        orderEmptyColumn.setColumn(\"\");\n        orderItems.add(orderEmptyColumn);\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByBefore() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFix() {\n        List<OrderItem> orderList = new ArrayList<>();\n        // 测试可能的 sql 注入 https://github.com/baomidou/mybatis-plus/issues/5745\n        orderList.add(OrderItem.asc(\"col umn\"));\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union select * from test2\", orderList);\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test UNION SELECT * FROM test2 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union all select * from test2\", orderList);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test UNION ALL SELECT * FROM test2 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFixWithWhere() {\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union select * from test2 where 1 = 1\", ITEMS);\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union all select * from test2 where 1 = 1 \", ITEMS);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION ALL SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorOrderByEmptyColumnFix() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/build.gradle",
    "content": "dependencies {\n    api \"com.github.jsqlparser:jsqlparser:4.9\"\n    api project(\":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-common\")\n    implementation \"${lib.\"slf4j-api\"}\"\n    implementation \"de.ruedigermoeller:fst:3.0.3\"\n    implementation \"com.github.ben-manes.caffeine:caffeine:2.9.3\"\n    testImplementation \"io.github.classgraph:classgraph:4.8.177\"\n    testImplementation \"${lib.\"spring-context-support\"}\"\n    testImplementation \"${lib.h2}\"\n    testImplementation group: 'com.google.guava', name: 'guava', version: '33.3.1-jre'\n\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/DynamicTableNameHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.create.index.CreateIndex;\nimport net.sf.jsqlparser.statement.create.view.CreateView;\nimport net.sf.jsqlparser.statement.drop.Drop;\nimport net.sf.jsqlparser.util.TablesNamesFinder;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 动态表名解析处理\n * <p>1.无法保留sql注释(例如 select * from test; --这是个查询 处理完会变成 select * from test)</p>\n * <p>2.无法保留语句分隔符;(例如 select * from test; 处理完会变成 select * from test )</p>\n * <p>3.如果使用转义符包裹了表名需要自行处理</p>\n * <p>4.select * from dual (不处理这个,自行忽略)</p>\n *\n * @author nieqiurong\n * @since 3.5.11\n */\npublic class DynamicTableNameHandler extends TablesNamesFinder {\n\n    private final String originSql;\n\n    private final TableNameHandler tableNameHandler;\n\n    private final Set<Table> set = new HashSet<>();\n\n    public DynamicTableNameHandler(String originSql, TableNameHandler tableNameHandler) {\n        this.originSql = originSql;\n        this.tableNameHandler = tableNameHandler;\n        init(false);\n    }\n\n    @Override\n    public void visit(CreateIndex createIndex) {\n        super.visit(createIndex.getTable());\n    }\n\n    @Override\n    public void visit(Drop drop) {\n        if(StringUtils.isNotBlank(drop.getType())){\n            String type = drop.getType().toUpperCase();\n            if (\"TABLE\".equals(type)) {\n                 super.visit(drop);\n            }\n        }\n    }\n\n    @Override\n    public void visit(CreateView createView) {\n        super.visit(createView.getSelect());\n    }\n\n    @Override\n    protected String extractTableName(Table table) {\n        String originalTableName = table.getName();\n        if (table.getASTNode() == null) {\n            return originalTableName;\n        }\n        if (set.add(table)) {\n            String tableName = tableNameHandler.dynamicTableName(originSql, originalTableName);\n            if (StringUtils.isNotBlank(tableName)) {\n                table.setName(tableName);\n                return tableName;\n            }\n        }\n        return originalTableName;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport net.sf.jsqlparser.JSQLParserException;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\n@FunctionalInterface\npublic interface JsqlParserFunction<T, R> {\n\n    R apply(T t) throws JSQLParserException;\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.JsqlParseCache;\nimport com.baomidou.mybatisplus.jsqlparser.JsqlParserThreadPool;\nimport lombok.Setter;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\nimport java.util.concurrent.ExecutorService;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\npublic class JsqlParserGlobal {\n\n\n    /**\n     * 默认线程数大小\n     *\n     * @since 3.5.6\n     * @deprecated {@link JsqlParserThreadPool#DEFAULT_THREAD_SIZE}\n     */\n    @Deprecated\n    public static final int DEFAULT_THREAD_SIZE = (Runtime.getRuntime().availableProcessors() + 1) / 2;\n\n    /**\n     * 默认解析处理线程池\n     * <p>注意: 由于项目情况,机器配置等不一样因素,请自行根据情况创建指定线程池.</p>\n     *\n     * @see java.util.concurrent.ThreadPoolExecutor\n     * @see #setExecutorService(ExecutorService)\n     * @see #setExecutorService(ExecutorService, boolean)\n     * @since 3.5.6\n     * @deprecated 3.5.11 后面不再公开此属性\n     */\n    @Deprecated\n    public static ExecutorService executorService;\n\n    @Setter\n    private static JsqlParserFunction<String, Statement> parserSingleFunc = sql -> CCJSqlParserUtil.parse(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParserFunction<String, Statements> parserMultiFunc = sql -> CCJSqlParserUtil.parseStatements(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParseCache jsqlParseCache;\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @since 3.5.11\n     */\n    public static void setExecutorService(ExecutorService executorService) {\n        JsqlParserGlobal.executorService = executorService;\n    }\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param addShutdownHook 是否注册退出关闭钩子\n     * @since 3.5.11\n     * @deprecated 3.5.12 推荐使用 {@link #setExecutorService(ExecutorService, Thread)}\n     */\n    @Deprecated\n    public static void setExecutorService(ExecutorService executorService, boolean addShutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (addShutdownHook) {\n            JsqlParserThreadPool.addShutdownHook(executorService);\n        }\n    }\n\n    /**\n     * 设置解析线程池\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param shutdownHook 关闭钩子\n     * @since 3.5.12\n     */\n    public static void setExecutorService(ExecutorService executorService, Thread shutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (shutdownHook != null) {\n            Runtime.getRuntime().addShutdownHook(shutdownHook);\n        }\n    }\n\n    /**\n     * 获取解析线程池(如果未自定义则返回默认的解析线程池)\n     *\n     * @return 解析线程池\n     * @since 3.5.11\n     */\n    public static ExecutorService getExecutorService() {\n        return JsqlParserGlobal.executorService == null ? JsqlParserThreadPool.getDefaultThreadPoolExecutor() : JsqlParserGlobal.executorService;\n    }\n\n    public static Statement parse(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserSingleFunc.apply(sql);\n        }\n        Statement statement = jsqlParseCache.getStatement(sql);\n        if (statement == null) {\n            statement = parserSingleFunc.apply(sql);\n            jsqlParseCache.putStatement(sql, statement);\n        }\n        return statement;\n    }\n\n    public static Statements parseStatements(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserMultiFunc.apply(sql);\n        }\n        Statements statements = jsqlParseCache.getStatements(sql);\n        if (statements == null) {\n            statements = parserMultiFunc.apply(sql);\n            jsqlParseCache.putStatements(sql, statements);\n        }\n        return statements;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\n/**\n * https://github.com/JSQLParser/JSqlParser\n *\n * @author miemie\n * @since 2020-06-22\n */\npublic abstract class JsqlParserSupport {\n\n    /**\n     * 日志\n     */\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n    public String parserSingle(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            return processParser(statement, 0, sql, obj);\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    public String parserMulti(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            // fixed github pull/295\n            StringBuilder sb = new StringBuilder();\n            Statements statements = JsqlParserGlobal.parseStatements(sql);\n            int i = 0;\n            for (Statement statement : statements) {\n                if (i > 0) {\n                    sb.append(StringPool.SEMICOLON);\n                }\n                sb.append(processParser(statement, i, sql, obj));\n                i++;\n            }\n            return sb.toString();\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    /**\n     * 执行 SQL 解析\n     *\n     * @param statement JsqlParser Statement\n     * @return sql\n     */\n    protected String processParser(Statement statement, int index, String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"SQL to parse, SQL: \" + sql);\n        }\n        if (statement instanceof Insert) {\n            this.processInsert((Insert) statement, index, sql, obj);\n        } else if (statement instanceof Select) {\n            this.processSelect((Select) statement, index, sql, obj);\n        } else if (statement instanceof Update) {\n            this.processUpdate((Update) statement, index, sql, obj);\n        } else if (statement instanceof Delete) {\n            this.processDelete((Delete) statement, index, sql, obj);\n        }\n        sql = statement.toString();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"parse the finished SQL: \" + sql);\n        }\n        return sql;\n    }\n\n    /**\n     * 新增\n     */\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 删除\n     */\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 更新\n     */\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 查询\n     */\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Executor;\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Caffeine 缓存实现抽象类\n *\n * @author miemie hubin\n * @since 2023-08-08\n */\npublic abstract class AbstractCaffeineJsqlParseCache implements JsqlParseCache {\n    protected final Log logger = LogFactory.getLog(this.getClass());\n    protected final Cache<String, byte[]> cache;\n    @Setter\n    protected boolean async = false;\n    @Setter\n    protected Executor executor;\n\n    public AbstractCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        this.cache = cache;\n    }\n\n    public AbstractCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        Caffeine<Object, Object> caffeine = Caffeine.newBuilder();\n        consumer.accept(caffeine);\n        this.cache = caffeine.build();\n    }\n\n    @Override\n    public void putStatement(String sql, Statement value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public void putStatements(String sql, Statements value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public Statement getStatement(String sql) {\n        return this.get(sql);\n    }\n\n    @Override\n    public Statements getStatements(String sql) {\n        return this.get(sql);\n    }\n\n    /**\n     * 获取解析对象，异常清空缓存逻辑\n     *\n     * @param sql 执行 SQL\n     * @return 返回泛型对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T get(String sql) {\n        byte[] bytes = cache.getIfPresent(sql);\n        if (null != bytes) {\n            try {\n                return (T) deserialize(sql, bytes);\n            } catch (Exception e) {\n                cache.invalidate(sql);\n                logger.error(\"deserialize error\", e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 存储解析对象\n     *\n     * @param sql   执行 SQL\n     * @param value 解析对象\n     */\n    protected void put(String sql, Object value) {\n        final byte[] serialVal = serialize(value);\n        if (async) {\n            if (executor != null) {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal), executor);\n            } else {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal));\n            }\n        } else {\n            cache.put(sql, serialVal);\n        }\n    }\n\n    /**\n     * 序列化\n     */\n    public abstract byte[] serialize(Object obj);\n\n    /**\n     * 反序列化\n     */\n    public abstract Object deserialize(String sql, byte[] bytes);\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport org.nustaq.serialization.FSTConfiguration;\n\n/**\n * Fst Factory\n *\n * @author miemie\n * @since 2023-08-06\n */\npublic class FstFactory {\n    private static final FstFactory FACTORY = new FstFactory();\n    private final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();\n\n    public static FstFactory getDefaultFactory() {\n        return FACTORY;\n    }\n\n    public FstFactory() {\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.AliasColumn.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AllValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnalyticExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnyComparisonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CaseExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CastExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CollateExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ConnectByRootOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateTimeLiteralExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DoubleValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ExtractExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.FilterOverImpl.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Function.class);\n        conf.registerClass(net.sf.jsqlparser.expression.HexValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.IntervalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcNamedParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonAggregateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunctionExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonKeyValuePair.class);\n        conf.registerClass(net.sf.jsqlparser.expression.KeepExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LongValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLGroupConcat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLIndexHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NextValExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NotExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NullValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NumericBind.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHierarchicalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleNamedFunctionParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OrderByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OverlapsCondition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Parenthesis.class);\n        conf.registerClass(net.sf.jsqlparser.expression.PartitionByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RangeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowGetExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SQLServerHints.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SignedExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.StringValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeKeyExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimestampValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimezoneExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TranscodingFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TrimFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.UserVariable.class);\n        conf.registerClass(net.sf.jsqlparser.expression.VariableAssignment.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WhenClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowElement.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowOffset.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowRange.class);\n        conf.registerClass(net.sf.jsqlparser.expression.XMLSerializeExpr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Addition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Concat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Division.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Modulo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Multiplication.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Subtraction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.AndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.OrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.XorExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Between.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ContainedBy.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Contains.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.DoubleAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.EqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExistsExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.FullTextSearch.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GeometryDistance.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.InExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsNullExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.JsonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.LikeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Matches.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MemberOfExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NamedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NotEqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.SimilarToExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin.class);\n        conf.registerClass(net.sf.jsqlparser.parser.ASTNodeAccessImpl.class);\n        conf.registerClass(net.sf.jsqlparser.parser.Token.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Column.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Sequence.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Synonym.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Table.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Block.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Commit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.TypeDefExpr.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DescribeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.Option.class);\n        conf.registerClass(net.sf.jsqlparser.statement.IfElseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.OutputClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.PurgeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ReferentialAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ResetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.RollbackStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SavepointStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowColumnsStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Statements.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UnsupportedStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.Alter.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropDefault.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSession.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSystemStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.RenameTableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.sequence.AlterSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.analyze.Analyze.class);\n        conf.registerClass(net.sf.jsqlparser.statement.comment.Comment.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.function.CreateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.index.CreateIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.procedure.CreateProcedure.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.schema.CreateSchema.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.sequence.CreateSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.synonym.CreateSynonym.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CheckConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColumnDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CreateTable.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ExcludeConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.ColumnParams.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.NamedConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.RowMovement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.AlterView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.CreateView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.delete.Delete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.drop.Drop.class);\n        conf.registerClass(net.sf.jsqlparser.statement.execute.Execute.class);\n        conf.registerClass(net.sf.jsqlparser.statement.grant.Grant.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.Insert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictTarget.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.Merge.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeDelete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeInsert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeUpdate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllTableColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Distinct.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ExceptOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Fetch.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.First.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ForClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.GroupByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.IntersectOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Join.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLJoinWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralSubSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Limit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.MinusOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Offset.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OptimizeFor.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OrderByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedFromItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Pivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PivotXml.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PlainSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SelectItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperationList.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Skip.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Top.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnPivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnionOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Values.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Wait.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithIsolation.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowIndexStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowTablesStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.truncate.Truncate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.Update.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.UpdateSet.class);\n        conf.registerClass(net.sf.jsqlparser.statement.upsert.Upsert.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiAndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiOrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.BinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ComparisonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.CreateFunctionalStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Select.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperation.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultipleExpression.class);\n    }\n\n    public byte[] asByteArray(Object obj) {\n        return conf.asByteArray(obj);\n    }\n\n    public Object asObject(byte[] bytes) {\n        return conf.asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Fst 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class FstSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public FstSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public FstSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return FstFactory.getDefaultFactory().asByteArray(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return FstFactory.getDefaultFactory().asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.baomidou.mybatisplus.core.toolkit.SerializationUtils;\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 jdk 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class JdkSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public JdkSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public JdkSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return SerializationUtils.serialize(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return SerializationUtils.deserialize(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\n/**\n * jsqlparser 缓存接口\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic interface JsqlParseCache {\n\n    void putStatement(String sql, Statement value);\n\n    void putStatements(String sql, Statements value);\n\n    Statement getStatement(String sql);\n\n    Statements getStatements(String sql);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.4.1 +\n */\npublic interface DataPermissionHandler {\n\n    /**\n     * 获取数据权限 SQL 片段\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会覆盖原有的条件表达式\n     */\n    Expression getSqlSegment(Expression where, String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\n\n/**\n * 支持多表的数据权限处理器\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic interface MultiDataPermissionHandler extends DataPermissionHandler {\n    /**\n     * 为兼容旧版数据权限处理器，继承了 {@link DataPermissionHandler} 但是新的多表数据权限处理又不会调用此方法，因此标记过时\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式\n     * @deprecated 新的多表数据权限处理不会调用此方法，因此标记过时\n     */\n    @Deprecated\n    @Override\n    default Expression getSqlSegment(Expression where, String mappedStatementId) {\n        return where;\n    }\n\n    /**\n     * 获取数据权限 SQL 片段。\n     * <p>旧的 {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} 方法第一个参数包含所有的 where 条件信息，如果 return 了 null 会覆盖原有的 where 数据，</p>\n     * <p>新版的 {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆盖原有的 where 数据，如果 return 了 null 则表示不追加任何 where 条件</p>\n     *\n     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名\n     * @param where             原有的 where 条件信息\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）\n     */\n    Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\n\nimport java.util.List;\n\n/**\n * 租户处理器（ TenantId 行级 ）\n *\n * @author hubin\n * @since 3.4.0\n */\npublic interface TenantLineHandler {\n\n    /**\n     * 获取租户 ID 值表达式，只支持单个 ID 值\n     * <p>\n     *\n     * @return 租户 ID 值表达式\n     */\n    Expression getTenantId();\n\n    /**\n     * 获取租户字段名\n     * <p>\n     * 默认字段名叫: tenant_id\n     *\n     * @return 租户字段名\n     */\n    default String getTenantIdColumn() {\n        return \"tenant_id\";\n    }\n\n    /**\n     * 根据表名判断是否忽略拼接多租户条件\n     * <p>\n     * 默认都要进行解析并拼接多租户条件\n     *\n     * @param tableName 表名\n     * @return 是否忽略, true:表示忽略，false:需要解析并拼接多租户条件\n     */\n    default boolean ignoreTable(String tableName) {\n        return false;\n    }\n\n    /**\n     * 忽略插入租户字段逻辑\n     *\n     * @param columns        插入字段\n     * @param tenantIdColumn 租户 ID 字段\n     * @return\n     */\n    default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {\n        return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.*;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExistsExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 多表条件处理基对象，从原有的 {@link TenantLineInnerInterceptor} 拦截器中提取出来\n *\n * @author houkunlin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 条件表达式追加模式 (默认放置最后,仅作用于update,delete,select)\n     *\n     * @since 3.5.11\n     */\n    private ExpressionAppendMode expressionAppendMode = ExpressionAppendMode.LAST;\n\n    protected void processSelectBody(Select selectBody, final String whereSegment) {\n        if (selectBody == null) {\n            return;\n        }\n        if (selectBody instanceof PlainSelect) {\n            processPlainSelect((PlainSelect) selectBody, whereSegment);\n        } else if (selectBody instanceof ParenthesedSelect) {\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processSelectBody(parenthesedSelect.getSelect(), whereSegment);\n        } else if (selectBody instanceof SetOperationList) {\n            SetOperationList operationList = (SetOperationList) selectBody;\n            List<Select> selectBodyList = operationList.getSelects();\n            if (CollectionUtils.isNotEmpty(selectBodyList)) {\n                selectBodyList.forEach(body -> processSelectBody(body, whereSegment));\n            }\n        }\n    }\n\n    /**\n     * delete update 语句 where 处理\n     */\n    protected Expression andExpression(Table table, Expression where, final String whereSegment) {\n        //获得where条件表达式\n        final Expression expression = buildTableExpression(table, where, whereSegment);\n        if (expression == null) {\n            return where;\n        }\n        if (where != null) {\n            if (where instanceof OrExpression) {\n                return appendExpression(new Parenthesis(where), expression);\n            } else {\n                return appendExpression(where, expression);\n            }\n        }\n        return expression;\n    }\n\n    /**\n     * 追加表达式，默认追加到后面，可以配置变量 {@link #expressionAppendMode} 来控制追加到前面还是后面\n     *\n     * @param currentExpression 原sql的条件表达式\n     * @param injectExpression  注入的表达式\n     * @return 追加了条件的完整表达式(where条件 / on条件)\n     * @since 3.5.11\n     */\n    protected Expression appendExpression(Expression currentExpression, Expression injectExpression) {\n        if (ExpressionAppendMode.LAST == expressionAppendMode || expressionAppendMode == null) {\n            return new AndExpression(currentExpression, injectExpression);\n        } else {\n            return new AndExpression(injectExpression, currentExpression);\n        }\n    }\n    /**\n     * 处理 PlainSelect\n     */\n    protected void processPlainSelect(final PlainSelect plainSelect, final String whereSegment) {\n        //#3087 github\n        List<SelectItem<?>> selectItems = plainSelect.getSelectItems();\n        if (CollectionUtils.isNotEmpty(selectItems)) {\n            selectItems.forEach(selectItem -> processSelectItem(selectItem, whereSegment));\n        }\n\n        // 处理 where 中的子查询\n        Expression where = plainSelect.getWhere();\n        processWhereSubSelect(where, whereSegment);\n\n        // 处理 fromItem\n        FromItem fromItem = plainSelect.getFromItem();\n        List<Table> list = processFromItem(fromItem, whereSegment);\n        List<Table> mainTables = new ArrayList<>(list);\n\n        // 处理 join\n        List<Join> joins = plainSelect.getJoins();\n        if (CollectionUtils.isNotEmpty(joins)) {\n            processJoins(mainTables, joins, whereSegment);\n        }\n\n        // 当有 mainTable 时，进行 where 条件追加\n        if (CollectionUtils.isNotEmpty(mainTables)) {\n            plainSelect.setWhere(builderExpression(where, mainTables, whereSegment));\n        }\n    }\n\n    private List<Table> processFromItem(FromItem fromItem, final String whereSegment) {\n        // 处理括号括起来的表达式\n//        while (fromItem instanceof ParenthesedFromItem) {\n//            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n//        }\n\n        List<Table> mainTables = new ArrayList<>();\n        // 无 join 时的处理逻辑\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            mainTables.add(fromTable);\n        } else if (fromItem instanceof ParenthesedFromItem) {\n            // SubJoin 类型则还需要添加上 where 条件\n            List<Table> tables = processSubJoin((ParenthesedFromItem) fromItem, whereSegment);\n            mainTables.addAll(tables);\n        } else {\n            // 处理下 fromItem\n            processOtherFromItem(fromItem, whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理where条件内的子查询\n     * <p>\n     * 支持如下:\n     * <ol>\n     *     <li>in</li>\n     *     <li>=</li>\n     *     <li>&gt;</li>\n     *     <li>&lt;</li>\n     *     <li>&gt;=</li>\n     *     <li>&lt;=</li>\n     *     <li>&lt;&gt;</li>\n     *     <li>EXISTS</li>\n     *     <li>NOT EXISTS</li>\n     * </ol>\n     * <p>\n     * 前提条件:\n     * 1. 子查询必须放在小括号中\n     * 2. 子查询一般放在比较操作符的右边\n     *\n     * @param where where 条件\n     */\n    protected void processWhereSubSelect(Expression where, final String whereSegment) {\n        if (where == null) {\n            return;\n        }\n        if (where instanceof FromItem) {\n            processOtherFromItem((FromItem) where, whereSegment);\n            return;\n        }\n        if (where.toString().indexOf(\"SELECT\") > 0) {\n            // 有子查询\n            if (where instanceof BinaryExpression) {\n                // 比较符号 , and , or , 等等\n                BinaryExpression expression = (BinaryExpression) where;\n                processWhereSubSelect(expression.getLeftExpression(), whereSegment);\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof InExpression) {\n                // in\n                InExpression expression = (InExpression) where;\n                Expression inExpression = expression.getRightExpression();\n                if (inExpression instanceof Select) {\n                    processSelectBody(((Select) inExpression), whereSegment);\n                }\n            } else if (where instanceof ExistsExpression) {\n                // exists\n                ExistsExpression expression = (ExistsExpression) where;\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof NotExpression) {\n                // not exists\n                NotExpression expression = (NotExpression) where;\n                processWhereSubSelect(expression.getExpression(), whereSegment);\n            } else if (where instanceof Parenthesis) {\n                Parenthesis expression = (Parenthesis) where;\n                processWhereSubSelect(expression.getExpression(), whereSegment);\n            }\n        }\n    }\n\n    protected void processSelectItem(SelectItem selectItem, final String whereSegment) {\n        Expression expression = selectItem.getExpression();\n        if (expression instanceof Select) {\n            processSelectBody(((Select) expression), whereSegment);\n        } else if (expression instanceof Function) {\n            processFunction((Function) expression, whereSegment);\n        } else if (expression instanceof ExistsExpression) {\n            ExistsExpression existsExpression = (ExistsExpression) expression;\n            processSelectBody((Select) existsExpression.getRightExpression(), whereSegment);\n        }\n    }\n\n    /**\n     * 处理函数\n     * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>\n     * <p> fixed gitee pulls/141</p>\n     *\n     * @param function\n     */\n    protected void processFunction(Function function, final String whereSegment) {\n        ExpressionList<?> parameters = function.getParameters();\n        if (parameters != null) {\n            parameters.forEach(expression -> {\n                if (expression instanceof Select) {\n                    processSelectBody(((Select) expression), whereSegment);\n                } else if (expression instanceof Function) {\n                    processFunction((Function) expression, whereSegment);\n                } else if (expression instanceof EqualsTo) {\n                    if (((EqualsTo) expression).getLeftExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getLeftExpression()), whereSegment);\n                    }\n                    if (((EqualsTo) expression).getRightExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getRightExpression()), whereSegment);\n                    }\n                }\n            });\n        }\n    }\n\n    /**\n     * 处理子查询等\n     */\n    protected void processOtherFromItem(FromItem fromItem, final String whereSegment) {\n        // 去除括号\n        while (fromItem instanceof ParenthesedFromItem) {\n            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n        }\n\n        if (fromItem instanceof ParenthesedSelect) {\n            Select subSelect = (Select) fromItem;\n            processSelectBody(subSelect, whereSegment);\n        }\n    }\n\n    /**\n     * 处理 sub join\n     *\n     * @param subJoin subJoin\n     * @return Table subJoin 中的主表\n     */\n    private List<Table> processSubJoin(ParenthesedFromItem subJoin, final String whereSegment) {\n        while (subJoin.getJoins() == null && subJoin.getFromItem() instanceof ParenthesedFromItem) {\n            subJoin = (ParenthesedFromItem) subJoin.getFromItem();\n        }\n        List<Table> tableList = processFromItem(subJoin.getFromItem(), whereSegment);\n        List<Table> mainTables = new ArrayList<>(tableList);\n        if (subJoin.getJoins() != null) {\n            processJoins(mainTables, subJoin.getJoins(), whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理 joins\n     *\n     * @param mainTables 可以为 null\n     * @param joins      join 集合\n     * @return List<Table> 右连接查询的 Table 列表\n     */\n    private List<Table> processJoins(List<Table> mainTables, List<Join> joins, final String whereSegment) {\n        // join 表达式中最终的主表\n        Table mainTable = null;\n        // 当前 join 的左表\n        Table leftTable = null;\n\n        if (mainTables.size() == 1) {\n            mainTable = mainTables.get(0);\n            leftTable = mainTable;\n        }\n\n        //对于 on 表达式写在最后的 join，需要记录下前面多个 on 的表名\n        Deque<List<Table>> onTableDeque = new LinkedList<>();\n        for (Join join : joins) {\n            // 处理 on 表达式\n            FromItem joinItem = join.getRightItem();\n\n            // 获取当前 join 的表，subJoint 可以看作是一张表\n            List<Table> joinTables = null;\n            if (joinItem instanceof Table) {\n                joinTables = new ArrayList<>();\n                joinTables.add((Table) joinItem);\n            } else if (joinItem instanceof ParenthesedFromItem) {\n                joinTables = processSubJoin((ParenthesedFromItem) joinItem, whereSegment);\n            }\n\n            if (joinTables != null && !joinTables.isEmpty()) {\n\n                // 如果是隐式内连接\n                if (join.isSimple()) {\n                    mainTables.addAll(joinTables);\n                    continue;\n                }\n\n                // 当前表是否忽略\n                Table joinTable = joinTables.get(0);\n\n                List<Table> onTables = null;\n                // 如果不要忽略，且是右连接，则记录下当前表\n                if (join.isRight()) {\n                    mainTable = joinTable;\n                    mainTables.clear();\n                    if (leftTable != null) {\n                        onTables = Collections.singletonList(leftTable);\n                    }\n                } else if (join.isInner()) {\n                    if (mainTable == null) {\n                        onTables = Collections.singletonList(joinTable);\n                    } else {\n                        onTables = Arrays.asList(mainTable, joinTable);\n                    }\n                    mainTable = null;\n                    mainTables.clear();\n                } else {\n                    onTables = Collections.singletonList(joinTable);\n                }\n\n                if (mainTable != null && !mainTables.contains(mainTable)) {\n                    mainTables.add(mainTable);\n                }\n\n                // 获取 join 尾缀的 on 表达式列表\n                Collection<Expression> originOnExpressions = join.getOnExpressions();\n                // 正常 join on 表达式只有一个，立刻处理\n                if (originOnExpressions.size() == 1 && onTables != null) {\n                    List<Expression> onExpressions = new LinkedList<>();\n                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables, whereSegment));\n                    join.setOnExpressions(onExpressions);\n                    leftTable = mainTable == null ? joinTable : mainTable;\n                    continue;\n                }\n                // 表名压栈，忽略的表压入 null，以便后续不处理\n                onTableDeque.push(onTables);\n                // 尾缀多个 on 表达式的时候统一处理\n                if (originOnExpressions.size() > 1) {\n                    Collection<Expression> onExpressions = new LinkedList<>();\n                    for (Expression originOnExpression : originOnExpressions) {\n                        List<Table> currentTableList = onTableDeque.poll();\n                        if (CollectionUtils.isEmpty(currentTableList)) {\n                            onExpressions.add(originOnExpression);\n                        } else {\n                            onExpressions.add(builderExpression(originOnExpression, currentTableList, whereSegment));\n                        }\n                    }\n                    join.setOnExpressions(onExpressions);\n                }\n                leftTable = joinTable;\n            } else {\n                processOtherFromItem(joinItem, whereSegment);\n                leftTable = null;\n            }\n        }\n\n        return mainTables;\n    }\n\n    /**\n     * 处理条件\n     */\n    protected Expression builderExpression(Expression currentExpression, List<Table> tables, final String whereSegment) {\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(tables)) {\n            return currentExpression;\n        }\n        // 构造每张表的条件\n        List<Expression> expressions = tables.stream()\n            .map(item -> buildTableExpression(item, currentExpression, whereSegment))\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList());\n\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(expressions)) {\n            return currentExpression;\n        }\n\n        // 注入的表达式\n        Expression injectExpression = expressions.get(0);\n        // 如果有多表，则用 and 连接\n        if (expressions.size() > 1) {\n            for (int i = 1; i < expressions.size(); i++) {\n                injectExpression = new AndExpression(injectExpression, expressions.get(i));\n            }\n        }\n\n        if (currentExpression == null) {\n            return injectExpression;\n        }\n        if (currentExpression instanceof OrExpression) {\n            return appendExpression(new Parenthesis(currentExpression), injectExpression);\n        } else {\n            return appendExpression(currentExpression, injectExpression);\n        }\n    }\n\n    /**\n     * 构建数据库表的查询条件\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径\n     * @return 需要拼接的新条件（不会覆盖原有的where条件，只会在原有条件上再加条件），为 null 则不加入新的条件\n     */\n    public abstract Expression buildTableExpression(final Table table, final Expression where, final String whereSegment);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BlockAttackInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Parenthesis;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.IsNullExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\n\n/**\n * 攻击 SQL 阻断解析器,防止全表更新与删除\n *\n * @author hubin\n * @since 3.4.0\n */\npublic class BlockAttackInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler handler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = handler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreBlockAttack(ms.getId())) {\n                return;\n            }\n            BoundSql boundSql = handler.boundSql();\n            parserMulti(boundSql.getSql(), null);\n        }\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        this.checkWhere(delete.getTable().getName(), delete.getWhere(), \"Prohibition of full table deletion\");\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        this.checkWhere(update.getTable().getName(), update.getWhere(), \"Prohibition of table update operation\");\n    }\n\n    protected void checkWhere(String tableName, Expression where, String ex) {\n        Assert.isFalse(this.fullMatch(where, this.getTableLogicField(tableName)), ex);\n    }\n\n    private boolean fullMatch(Expression where, String logicField) {\n        if (where == null) {\n            return true;\n        }\n        if (StringUtils.isNotBlank(logicField)) {\n\n            if (where instanceof BinaryExpression) {\n                BinaryExpression binaryExpression = (BinaryExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField) || StringUtils.equals(binaryExpression.getRightExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n\n            if (where instanceof IsNullExpression) {\n                IsNullExpression binaryExpression = (IsNullExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n        }\n\n        if (where instanceof EqualsTo) {\n            // example: 1=1\n            EqualsTo equalsTo = (EqualsTo) where;\n            return StringUtils.equals(equalsTo.getLeftExpression().toString(), equalsTo.getRightExpression().toString());\n        } else if (where instanceof NotEqualsTo) {\n            // example: 1 != 2\n            NotEqualsTo notEqualsTo = (NotEqualsTo) where;\n            return !StringUtils.equals(notEqualsTo.getLeftExpression().toString(), notEqualsTo.getRightExpression().toString());\n        } else if (where instanceof OrExpression) {\n\n            OrExpression orExpression = (OrExpression) where;\n            return fullMatch(orExpression.getLeftExpression(), logicField) || fullMatch(orExpression.getRightExpression(), logicField);\n        } else if (where instanceof AndExpression) {\n\n            AndExpression andExpression = (AndExpression) where;\n            return fullMatch(andExpression.getLeftExpression(), logicField) && fullMatch(andExpression.getRightExpression(), logicField);\n        } else if (where instanceof Parenthesis) {\n            // example: (1 = 1)\n            Parenthesis parenthesis = (Parenthesis) where;\n            return fullMatch(parenthesis.getExpression(), logicField);\n        }\n\n        return false;\n    }\n\n    /**\n     * 获取表名中的逻辑删除字段\n     *\n     * @param tableName 表名\n     * @return 逻辑删除字段\n     */\n    private String getTableLogicField(String tableName) {\n        if (StringUtils.isBlank(tableName)) {\n            return StringPool.EMPTY;\n        }\n\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);\n        if (tableInfo == null || !tableInfo.isWithLogicDelete() || tableInfo.getLogicDeleteFieldInfo() == null) {\n            return StringPool.EMPTY;\n        }\n        return tableInfo.getLogicDeleteFieldInfo().getColumn();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataChangeRecorderInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.io.Reader;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport net.sf.jsqlparser.statement.select.Values;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.SystemMetaObject;\nimport org.apache.ibatis.scripting.defaults.DefaultParameterHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.baomidou.mybatisplus.annotation.IEnum;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\n\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.JdbcParameter;\nimport net.sf.jsqlparser.expression.RowConstructor;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.AllColumns;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SelectItem;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\n\n/**\n * <p>\n * 数据变动记录插件\n * 默认会生成一条log，格式：\n * ----------------------INSERT LOG------------------------------\n * </p>\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"insert\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\",\n * \"TEST_ID\": \"null->1561666810058739714\",\n * \"AGE\": \"null->THREE\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n * <p>\n * * ----------------------UPDATE LOG------------------------------\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"update\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"TEST_ID\": \"102\",\n * \"AGE\": \"2->THREE\",\n * \"FIRSTNAME\": \"DOU.HAO->{\\\"json\\\":\\\"abc\\\"}\",\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n *\n * @author yuxiaobin\n * @deprecated 3.5.10 问题太多,计划移除\n * @date 2022-8-21\n */\n@Deprecated\npublic class DataChangeRecorderInnerInterceptor implements InnerInterceptor {\n\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @SuppressWarnings(\"unused\")\n    public static final String IGNORED_TABLE_COLUMN_PROPERTIES = \"ignoredTableColumns\";\n\n    private final Map<String, Set<String>> ignoredTableColumns = new ConcurrentHashMap<>();\n    private final Set<String> ignoreAllColumns = new HashSet<>();//全部表的这些字段名，INSERT/UPDATE都忽略，delete暂时保留\n    //批量更新上限, 默认一次最多1000条\n    private int BATCH_UPDATE_LIMIT = 1000;\n    private boolean batchUpdateLimitationOpened = false;\n    private final Map<String, Integer> BATCH_UPDATE_LIMIT_MAP = new ConcurrentHashMap<>();//表名->批量更新上限\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        final BoundSql boundSql = mpSh.boundSql();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            OperationResult operationResult;\n            long startTs = System.currentTimeMillis();\n            try {\n                Statement statement = JsqlParserGlobal.parse(mpBs.sql());\n                if (statement instanceof Insert) {\n                    operationResult = processInsert((Insert) statement, mpSh.boundSql());\n                } else if (statement instanceof Update) {\n                    operationResult = processUpdate((Update) statement, ms, boundSql, connection);\n                } else if (statement instanceof Delete) {\n                    operationResult = processDelete((Delete) statement, ms, boundSql, connection);\n                } else {\n                    logger.info(\"other operation sql={}\", mpBs.sql());\n                    return;\n                }\n            } catch (Exception e) {\n                if (e instanceof DataUpdateLimitationException) {\n                    throw (DataUpdateLimitationException) e;\n                }\n                logger.error(\"Unexpected error for mappedStatement={}, sql={}\", ms.getId(), mpBs.sql(), e);\n                return;\n            }\n            long costThis = System.currentTimeMillis() - startTs;\n            if (operationResult != null) {\n                operationResult.setCost(costThis);\n                dealOperationResult(operationResult);\n            }\n        }\n    }\n\n    /**\n     * 判断哪些SQL需要处理\n     * 默认INSERT/UPDATE/DELETE语句\n     *\n     * @param sql\n     * @return\n     */\n    protected boolean allowProcess(String sql) {\n        String sqlTrim = sql.trim().toUpperCase();\n        return sqlTrim.startsWith(\"INSERT\") || sqlTrim.startsWith(\"UPDATE\") || sqlTrim.startsWith(\"DELETE\");\n    }\n\n    /**\n     * 处理数据更新结果，默认打印\n     *\n     * @param operationResult\n     */\n    protected void dealOperationResult(OperationResult operationResult) {\n        logger.info(\"{}\", operationResult);\n    }\n\n    public OperationResult processInsert(Insert insertStmt, BoundSql boundSql) {\n        String operation = SqlCommandType.INSERT.name().toLowerCase();\n        Table table = insertStmt.getTable();\n        String tableName = table.getName();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, insertStmt);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), null, updatedColumnDatas));\n        return result;\n    }\n\n    public OperationResult processUpdate(Update updateStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Expression where = updateStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        Table table = updateStmt.getTable();\n        String tableName = table.getName();\n        String operation = SqlCommandType.UPDATE.name().toLowerCase();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        selectBody.setFromItem(table);\n        List<Column> updateColumns = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateColumns.addAll(updateSet.getColumns());\n        }\n        Columns2SelectItemsResult buildColumns2SelectItems = buildColumns2SelectItems(tableName, updateColumns);\n        selectBody.setSelectItems(buildColumns2SelectItems.getSelectItems());\n        selectBody.setWhere(where);\n        SelectItem<PlainSelect> plainSelectSelectItem = new SelectItem<>(selectBody);\n\n        BoundSql boundSql4Select = new BoundSql(mappedStatement.getConfiguration(), plainSelectSelectItem.toString(),\n            prepareParameterMapping4Select(boundSql.getParameterMappings(), updateStmt),\n            boundSql.getParameterObject());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n        Map<String, Object> additionalParameters = mpBoundSql.additionalParameters();\n        if (additionalParameters != null && !additionalParameters.isEmpty()) {\n            for (Map.Entry<String, Object> ety : additionalParameters.entrySet()) {\n                boundSql4Select.setAdditionalParameter(ety.getKey(), ety.getValue());\n            }\n        }\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, updateStmt);\n        OriginalDataObj originalData = buildOriginalObjectData(updatedColumnDatas, selectBody, buildColumns2SelectItems.getPk(), mappedStatement, boundSql4Select, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), originalData, updatedColumnDatas));\n        return result;\n    }\n\n    private Optional<OperationResult> ignoredTableColumns(String table, String operation) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(table.toUpperCase());\n        if (ignoredColumns != null) {\n            if (ignoredColumns.stream().anyMatch(\"*\"::equals)) {\n                OperationResult result = new OperationResult();\n                result.setOperation(operation);\n                result.setTableName(table + \":*\");\n                result.setRecordStatus(false);\n                return Optional.of(result);\n            }\n        }\n        return Optional.empty();\n    }\n\n    private TableInfo getTableInfoByTableName(String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                return tableInfo;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 将update SET部分的jdbc参数去除\n     *\n     * @param originalMappingList 这里只会包含JdbcParameter参数\n     * @param updateStmt\n     * @return\n     */\n    private List<ParameterMapping> prepareParameterMapping4Select(List<ParameterMapping> originalMappingList, Update updateStmt) {\n        List<Expression> updateValueExpressions = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateValueExpressions.addAll(updateSet.getValues());\n        }\n        int removeParamCount = 0;\n        for (Expression expression : updateValueExpressions) {\n            if (expression instanceof JdbcParameter) {\n                ++removeParamCount;\n            }\n        }\n        return originalMappingList.subList(removeParamCount, originalMappingList.size());\n    }\n\n    protected Map<String, Object> getUpdatedColumnDatas(String tableName, BoundSql updateSql, Statement statement) {\n        Map<String, Object> columnNameValMap = new HashMap<>(updateSql.getParameterMappings().size());\n        Map<Integer, String> columnSetIndexMap = new HashMap<>(updateSql.getParameterMappings().size());\n        List<Column> selectItemsFromUpdateSql = new ArrayList<>();\n        if (statement instanceof Update) {\n            Update updateStmt = (Update) statement;\n            int index = 0;\n            for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n                selectItemsFromUpdateSql.addAll(updateSet.getColumns());\n                final ExpressionList<Expression> updateList = (ExpressionList<Expression>) updateSet.getValues();\n                for (int i = 0; i < updateList.size(); ++i) {\n                    Expression updateExps = updateList.get(i);\n                    if (!(updateExps instanceof JdbcParameter)) {\n                        columnNameValMap.put(updateSet.getColumns().get(i).getColumnName().toUpperCase(), updateExps.toString());\n                    }\n                    columnSetIndexMap.put(index++, updateSet.getColumns().get(i).getColumnName().toUpperCase());\n                }\n            }\n        } else if (statement instanceof Insert) {\n            Insert insert = (Insert) statement;\n            selectItemsFromUpdateSql.addAll(insert.getColumns());\n            columnNameValMap.putAll(detectInsertColumnValuesNonJdbcParameters(insert));\n        }\n        Map<String, String> relatedColumnsUpperCaseWithoutUnderline = new HashMap<>(selectItemsFromUpdateSql.size(), 1);\n        for (Column item : selectItemsFromUpdateSql) {\n            //FIRSTNAME: FIRST_NAME/FIRST-NAME/FIRST$NAME/FIRST.NAME\n            relatedColumnsUpperCaseWithoutUnderline.put(item.getColumnName().replaceAll(\"[._\\\\-$]\", \"\").toUpperCase(), item.getColumnName().toUpperCase());\n        }\n        MetaObject metaObject = SystemMetaObject.forObject(updateSql.getParameterObject());\n        int index = 0;\n        for (ParameterMapping parameterMapping : updateSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                ++index;\n                continue;\n            }\n            String[] arr = propertyName.split(\"\\\\.\");\n            String propertyNameTrim = arr[arr.length - 1].replace(\"_\", \"\").toUpperCase();\n            try {\n                final String columnName = columnSetIndexMap.getOrDefault(index++, getColumnNameByProperty(propertyNameTrim, tableName));\n                if (relatedColumnsUpperCaseWithoutUnderline.containsKey(propertyNameTrim)) {\n                    final String colkey = relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim);\n                    Object valObj = metaObject.getValue(propertyName);\n                    if (valObj instanceof IEnum) {\n                        valObj = ((IEnum<?>) valObj).getValue();\n                    } else if (valObj instanceof Enum) {\n                        valObj = getEnumValue((Enum) valObj);\n                    }\n                    if (columnNameValMap.containsKey(colkey)) {\n                        columnNameValMap.put(relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim), String.valueOf(columnNameValMap.get(colkey)).replace(\"?\", valObj == null ? \"\" : valObj.toString()));\n                    }\n                    if (columnName != null && !columnNameValMap.containsKey(columnName)) {\n                        columnNameValMap.put(columnName, valObj);\n                    }\n                } else {\n                    if (columnName != null) {\n                        columnNameValMap.put(columnName, metaObject.getValue(propertyName));\n                    }\n                }\n            } catch (Exception e) {\n                logger.warn(\"get value error,propertyName:{},parameterMapping:{}\", propertyName, parameterMapping);\n            }\n        }\n        dealWithUpdateWrapper(columnSetIndexMap, columnNameValMap, updateSql);\n        return columnNameValMap;\n    }\n\n    /**\n     * @param originalDataObj\n     * @return\n     */\n    private List<DataChangedRecord> compareAndGetUpdatedColumnDatas(String tableName, OriginalDataObj originalDataObj, Map<String, Object> columnNameValMap) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(tableName.toUpperCase());\n        if (originalDataObj == null || originalDataObj.isEmpty()) {\n            DataChangedRecord oneRecord = new DataChangedRecord();\n            List<DataColumnChangeResult> updateColumns = new ArrayList<>(columnNameValMap.size());\n            for (Map.Entry<String, Object> ety : columnNameValMap.entrySet()) {\n                String columnName = ety.getKey();\n                if ((ignoredColumns == null || !ignoredColumns.contains(columnName)) && !ignoreAllColumns.contains(columnName)) {\n                    updateColumns.add(DataColumnChangeResult.constrcutByUpdateVal(columnName, ety.getValue()));\n                }\n            }\n            oneRecord.setUpdatedColumns(updateColumns);\n//            oneRecord.setUpdatedColumns(Collections.EMPTY_LIST);\n            return Collections.singletonList(oneRecord);\n        }\n        List<DataChangedRecord> originalDataList = originalDataObj.getOriginalDataObj();\n        List<DataChangedRecord> updateDataList = new ArrayList<>(originalDataList.size());\n        for (DataChangedRecord originalData : originalDataList) {\n            if (originalData.hasUpdate(columnNameValMap, ignoredColumns, ignoreAllColumns)) {\n                updateDataList.add(originalData);\n            }\n        }\n        return updateDataList;\n    }\n\n    private Object getEnumValue(Enum enumVal) {\n        Optional<String> enumValueFieldName = MybatisEnumTypeHandler.findEnumValueFieldName(enumVal.getClass());\n        if (enumValueFieldName.isPresent()) {\n            return SystemMetaObject.forObject(enumVal).getValue(enumValueFieldName.get());\n        }\n        return enumVal;\n\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    private void dealWithUpdateWrapper(Map<Integer, String> columnSetIndexMap, Map<String, Object> columnNameValMap, BoundSql updateSql) {\n        if (columnSetIndexMap.size() <= columnNameValMap.size()) {\n            return;\n        }\n        MetaObject mpgenVal = SystemMetaObject.forObject(updateSql.getParameterObject());\n        if(!mpgenVal.hasGetter(Constants.WRAPPER)){\n            return;\n        }\n        Object ew = mpgenVal.getValue(Constants.WRAPPER);\n        if (ew instanceof UpdateWrapper || ew instanceof LambdaUpdateWrapper) {\n            final String sqlSet = ew instanceof UpdateWrapper ? ((UpdateWrapper) ew).getSqlSet() : ((LambdaUpdateWrapper) ew).getSqlSet();// columnName=#{val}\n            if (sqlSet == null) {\n                return;\n            }\n            MetaObject ewMeta = SystemMetaObject.forObject(ew);\n            final Map paramNameValuePairs = (Map) ewMeta.getValue(\"paramNameValuePairs\");\n            String[] setItems = sqlSet.split(\",\");\n            for (String setItem : setItems) {\n                //age=#{ew.paramNameValuePairs.MPGENVAL1}\n                String[] nameAndValuePair = setItem.split(\"=\", 2);\n                if (nameAndValuePair.length == 2) {\n                    String setColName = nameAndValuePair[0].trim().toUpperCase();\n                    String setColVal = nameAndValuePair[1].trim();//#{.mp}\n                    if (columnSetIndexMap.containsValue(setColName)) {\n                        String[] mpGenKeyArray = setColVal.split(\"\\\\.\");\n                        String mpGenKey = mpGenKeyArray[mpGenKeyArray.length - 1].replace(\"}\", \"\");\n                        final Object setVal = paramNameValuePairs.get(mpGenKey);\n                        if (setVal instanceof IEnum) {\n                            columnNameValMap.put(setColName, String.valueOf(((IEnum<?>) setVal).getValue()));\n                        } else {\n                            columnNameValMap.put(setColName, setVal);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private Map<String, String> detectInsertColumnValuesNonJdbcParameters(Insert insert) {\n        Map<String, String> columnNameValMap = new HashMap<>(4);\n        final Select select = insert.getSelect();\n        List<Column> columns = insert.getColumns();\n        if (select instanceof SetOperationList) {\n            SetOperationList setOperationList = (SetOperationList) select;\n            final List<Select> selects = setOperationList.getSelects();\n            if (CollectionUtils.isEmpty(selects)) {\n                return columnNameValMap;\n            }\n            final Select selectBody = selects.get(0);\n            if (!(selectBody instanceof Values)) {\n                return columnNameValMap;\n            }\n            Values valuesStatement = (Values) selectBody;\n            if (valuesStatement.getExpressions() instanceof ExpressionList) {\n                ExpressionList expressionList = valuesStatement.getExpressions();\n                List<Expression> expressions = expressionList;\n                for (Expression expression : expressions) {\n                    if (expression instanceof RowConstructor) {\n                        final ExpressionList exprList = ((RowConstructor) expression);\n                        final List<Expression> insertExpList = exprList;\n                        for (int i = 0; i < insertExpList.size(); ++i) {\n                            Expression e = insertExpList.get(i);\n                            if (!(e instanceof JdbcParameter)) {\n                                final String columnName = columns.get(i).getColumnName();\n                                final String val = e.toString();\n                                columnNameValMap.put(columnName, val);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return columnNameValMap;\n    }\n\n    private String getColumnNameByProperty(String propertyName, String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                final List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n                if (CollectionUtils.isEmpty(fieldList)) {\n                    return propertyName;\n                }\n                for (TableFieldInfo tableFieldInfo : fieldList) {\n                    if (propertyName.equalsIgnoreCase(tableFieldInfo.getProperty())) {\n                        return tableFieldInfo.getColumn().toUpperCase();\n                    }\n                }\n                return propertyName;\n            }\n        }\n        return propertyName;\n    }\n\n\n    private Map<String, Object> buildParameterObjectMap(BoundSql boundSql) {\n        MetaObject metaObject = PluginUtils.getMetaObject(boundSql.getParameterObject());\n        Map<String, Object> propertyValMap = new HashMap<>(boundSql.getParameterMappings().size());\n        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                continue;\n            }\n            Object propertyValue = metaObject.getValue(propertyName);\n            propertyValMap.put(propertyName, propertyValue);\n        }\n        return propertyValMap;\n\n    }\n\n\n    private String buildOriginalData(Select selectStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            final ResultSetMetaData metaData = resultSet.getMetaData();\n            int columnCount = metaData.getColumnCount();\n            StringBuilder sb = new StringBuilder(\"[\");\n            int count = 0;\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch delete limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                sb.append(\"{\");\n                for (int i = 1; i <= columnCount; ++i) {\n                    sb.append(\"\\\"\").append(metaData.getColumnName(i)).append(\"\\\":\\\"\");\n                    Object res = resultSet.getObject(i);\n                    if (res instanceof Clob) {\n                        sb.append(DataColumnChangeResult.convertClob((Clob) res));\n                    } else {\n                        sb.append(res);\n                    }\n                    sb.append(\"\\\",\");\n                }\n                sb.replace(sb.length() - 1, sb.length(), \"}\");\n            }\n            sb.append(\"]\");\n            resultSet.close();\n            return sb.toString();\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe deleted for selectStmt={}\", selectStmt, e);\n            return \"failed to get original data\";\n        }\n    }\n\n    private OriginalDataObj buildOriginalObjectData(Map<String, Object> updatedColumnDatas, Select selectStmt, Column pk, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            List<DataChangedRecord> originalObjectDatas = new LinkedList<>();\n            int count = 0;\n\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch update limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                originalObjectDatas.add(prepareOriginalDataObj(updatedColumnDatas, resultSet, pk));\n            }\n            OriginalDataObj result = new OriginalDataObj();\n            result.setOriginalDataObj(originalObjectDatas);\n            resultSet.close();\n            return result;\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe updated for selectStmt={}\", selectStmt, e);\n            return new OriginalDataObj();\n        }\n    }\n\n    /**\n     * 防止出现全表批量更新\n     * 默认一次更新不超过1000条\n     *\n     * @param selectStmt\n     * @param count\n     * @return\n     */\n    private boolean checkTableBatchLimitExceeded(Select selectStmt, int count) {\n        if (!batchUpdateLimitationOpened) {\n            return false;\n        }\n        final PlainSelect selectBody = (PlainSelect) selectStmt;\n        final FromItem fromItem = selectBody.getFromItem();\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            final String tableName = fromTable.getName().toUpperCase();\n            if (!BATCH_UPDATE_LIMIT_MAP.containsKey(tableName)) {\n                if (count > BATCH_UPDATE_LIMIT) {\n                    logger.error(\"batch update limit exceed for tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                        tableName, BATCH_UPDATE_LIMIT, count);\n                    return true;\n                }\n                return false;\n            }\n            final Integer limit = BATCH_UPDATE_LIMIT_MAP.get(tableName);\n            if (count > limit) {\n                logger.error(\"batch update limit exceed for configured tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                    tableName, limit, count);\n                return true;\n            }\n            return false;\n        }\n        return count > BATCH_UPDATE_LIMIT;\n    }\n\n\n    /**\n     * get records : include related column with original data in DB\n     *\n     * @param resultSet\n     * @param pk\n     * @return\n     * @throws SQLException\n     */\n    private DataChangedRecord prepareOriginalDataObj(Map<String, Object> updatedColumnDatas, ResultSet resultSet, Column pk) throws SQLException {\n        final ResultSetMetaData metaData = resultSet.getMetaData();\n        int columnCount = metaData.getColumnCount();\n        List<DataColumnChangeResult> originalColumnDatas = new LinkedList<>();\n        DataColumnChangeResult pkval = null;\n        for (int i = 1; i <= columnCount; ++i) {\n            String columnName = metaData.getColumnName(i).toUpperCase();\n            DataColumnChangeResult col;\n            Object updateVal = updatedColumnDatas.get(columnName);\n            if (updateVal != null && updateVal.getClass().getCanonicalName().startsWith(\"java.\")) {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i, updateVal.getClass()));\n            } else {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i));\n            }\n            if (pk != null && columnName.equalsIgnoreCase(pk.getColumnName())) {\n                pkval = col;\n            } else {\n                originalColumnDatas.add(col);\n            }\n        }\n        DataChangedRecord changedRecord = new DataChangedRecord();\n        changedRecord.setOriginalColumnDatas(originalColumnDatas);\n        if (pkval != null) {\n            changedRecord.setPkColumnName(pkval.getColumnName());\n            changedRecord.setPkColumnVal(pkval.getOriginalValue());\n        }\n        return changedRecord;\n    }\n\n\n    private Columns2SelectItemsResult buildColumns2SelectItems(String tableName, List<Column> columns) {\n        if (columns == null || columns.isEmpty()) {\n            return Columns2SelectItemsResult.build(Collections.singletonList(new SelectItem<>(new AllColumns())), 0);\n        }\n        List<SelectItem<?>> selectItems = new ArrayList<>(columns.size());\n        for (Column column : columns) {\n            selectItems.add(new SelectItem<>(column));\n        }\n        TableInfo tableInfo = getTableInfoByTableName(tableName);\n        if (tableInfo == null || StringUtils.isBlank(tableInfo.getKeyColumn())) {\n            return Columns2SelectItemsResult.build(selectItems, 0);\n        }\n        Column pk = new Column(tableInfo.getKeyColumn());\n        selectItems.add(new SelectItem<>(pk));\n        Columns2SelectItemsResult result = Columns2SelectItemsResult.build(selectItems, 1);\n        result.setPk(pk);\n        return result;\n    }\n\n    private String buildParameterObject(BoundSql boundSql) {\n        Object paramObj = boundSql.getParameterObject();\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"{\");\n        if (paramObj instanceof Map) {\n            Map<String, Object> paramMap = (Map<String, Object>) paramObj;\n            int index = 1;\n            boolean hasParamIndex = false;\n            String key;\n            while (paramMap.containsKey((key = \"param\" + index))) {\n                Object paramIndex = paramMap.get(key);\n                sb.append(\"\\\"\").append(key).append(\"\\\"\").append(\":\").append(\"\\\"\").append(paramIndex).append(\"\\\"\").append(\",\");\n                hasParamIndex = true;\n                ++index;\n            }\n            if (hasParamIndex) {\n                sb.delete(sb.length() - 1, sb.length());\n                sb.append(\"}\");\n                return sb.toString();\n            }\n            for (Map.Entry<String, Object> ety : paramMap.entrySet()) {\n                sb.append(\"\\\"\").append(ety.getKey()).append(\"\\\"\").append(\":\").append(\"\\\"\").append(ety.getValue()).append(\"\\\"\").append(\",\");\n            }\n            sb.delete(sb.length() - 1, sb.length());\n            sb.append(\"}\");\n            return sb.toString();\n        }\n        sb.append(\"param:\").append(paramObj);\n        sb.append(\"}\");\n        return sb.toString();\n    }\n\n    public OperationResult processDelete(Delete deleteStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Table table = deleteStmt.getTable();\n        Expression where = deleteStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        selectBody.setFromItem(table);\n        selectBody.setSelectItems(Collections.singletonList(new SelectItem<>((new AllColumns()))));\n        selectBody.setWhere(where);\n        String originalData = buildOriginalData(selectBody, mappedStatement, boundSql, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(\"delete\");\n        result.setTableName(table.getName());\n        result.setRecordStatus(originalData.startsWith(\"[\"));\n        result.setChangedData(originalData);\n        return result;\n    }\n\n    /**\n     * 设置批量更新记录条数上限\n     *\n     * @param limit\n     * @return\n     */\n    public DataChangeRecorderInnerInterceptor setBatchUpdateLimit(int limit) {\n        this.BATCH_UPDATE_LIMIT = limit;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor openBatchUpdateLimitation() {\n        this.batchUpdateLimitationOpened = true;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor configTableLimitation(String tableName, int limit) {\n        this.BATCH_UPDATE_LIMIT_MAP.put(tableName.toUpperCase(), limit);\n        return this;\n    }\n\n    /**\n     * ignoredColumns = TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2; TABLE3.*; *.COLUMN1,COLUMN2\n     * 多个表用分号分隔\n     * TABLE_NAME1.COLUMN1,COLUMN2 : 表示忽略这个表的这2个字段\n     * TABLE3.*: 表示忽略这张表的INSERT/UPDATE，delete暂时还保留\n     * *.COLUMN1,COLUMN2:表示所有表的这个2个字段名都忽略\n     *\n     * @param properties\n     */\n    @Override\n    public void setProperties(Properties properties) {\n\n        String ignoredTableColumns = properties.getProperty(\"ignoredTableColumns\");\n        if (ignoredTableColumns == null || ignoredTableColumns.trim().isEmpty()) {\n            return;\n        }\n        String[] array = ignoredTableColumns.split(\";\");\n        for (String table : array) {\n            int index = table.indexOf(\".\");\n            if (index == -1) {\n                logger.warn(\"invalid data={} for ignoredColumns, format should be TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2;\", table);\n                continue;\n            }\n            String tableName = table.substring(0, index).trim().toUpperCase();\n            String[] columnArray = table.substring(index + 1).split(\",\");\n            Set<String> columnSet = new HashSet<>(columnArray.length);\n            for (String column : columnArray) {\n                column = column.trim().toUpperCase();\n                if (column.isEmpty()) {\n                    continue;\n                }\n                columnSet.add(column);\n            }\n            if (\"*\".equals(tableName)) {\n                ignoreAllColumns.addAll(columnSet);\n            } else {\n                this.ignoredTableColumns.put(tableName, columnSet);\n            }\n        }\n    }\n\n    @Data\n    public static class OperationResult {\n\n        private String operation;\n        private boolean recordStatus;\n        private String tableName;\n        private String changedData;\n        /**\n         * cost for this plugin, ms\n         */\n        private long cost;\n\n        public void buildDataStr(List<DataChangedRecord> records) {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"[\");\n            for (DataChangedRecord r : records) {\n                sb.append(r.generateUpdatedDataStr()).append(\",\");\n            }\n            if (sb.length() == 1) {\n                sb.append(\"]\");\n                changedData = sb.toString();\n                return;\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"]\");\n            changedData = sb.toString();\n        }\n\n        @Override\n        public String toString() {\n            return \"{\" +\n                \"\\\"tableName\\\":\\\"\" + tableName + \"\\\",\" +\n                \"\\\"operation\\\":\\\"\" + operation + \"\\\",\" +\n                \"\\\"recordStatus\\\":\\\"\" + recordStatus + \"\\\",\" +\n                \"\\\"changedData\\\":\" + changedData + \",\" +\n                \"\\\"cost(ms)\\\":\" + cost + \"}\";\n        }\n    }\n\n    @Data\n    public static class Columns2SelectItemsResult {\n\n        private Column pk;\n        /**\n         * all column with additional columns: ID, etc.\n         */\n        private List<SelectItem<?>> selectItems;\n        /**\n         * newly added column count from meta data.\n         */\n        private int additionalItemCount;\n\n        public static Columns2SelectItemsResult build(List<SelectItem<?>> selectItems, int additionalItemCount) {\n            Columns2SelectItemsResult result = new Columns2SelectItemsResult();\n            result.setSelectItems(selectItems);\n            result.setAdditionalItemCount(additionalItemCount);\n            return result;\n        }\n    }\n\n    @Data\n    public static class OriginalDataObj {\n\n        private List<DataChangedRecord> originalDataObj;\n\n        public boolean isEmpty() {\n            return originalDataObj == null || originalDataObj.isEmpty();\n        }\n\n    }\n\n    @Data\n    public static class DataColumnChangeResult {\n\n        private String columnName;\n        private Object originalValue;\n        private Object updateValue;\n\n        @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n        public boolean isDataChanged(Object updateValue) {\n            if (Objects.equals(originalValue, updateValue)) {\n                return false;\n            }\n            if (originalValue instanceof Clob) {\n                String originalStr = convertClob((Clob) originalValue);\n                setOriginalValue(originalStr);\n                return !originalStr.equals(updateValue);\n            }\n            if (originalValue instanceof Comparable) {\n                Comparable original = (Comparable) originalValue;\n                if (original.getClass().isInstance(updateValue)) {\n                    Comparable update = (Comparable) updateValue;\n                    return original.compareTo(update) != 0;\n                }\n            }\n            return true;\n        }\n\n        public static String convertClob(Clob clobObj) {\n            try {\n                return clobObj.getSubString(0, (int) clobObj.length());\n            } catch (Exception e) {\n                try (Reader is = clobObj.getCharacterStream()) {\n                    char[] chars = new char[64];\n                    int readChars;\n                    StringBuilder sb = new StringBuilder();\n                    while ((readChars = is.read(chars)) != -1) {\n                        sb.append(chars, 0, readChars);\n                    }\n                    return sb.toString();\n                } catch (Exception e2) {\n                    //ignored\n                    return \"unknown clobObj\";\n                }\n            }\n        }\n\n        public static DataColumnChangeResult constrcutByUpdateVal(String columnName, Object updateValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setUpdateValue(updateValue);\n            return res;\n        }\n\n        public static DataColumnChangeResult constrcutByOriginalVal(String columnName, Object originalValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setOriginalValue(originalValue);\n            return res;\n        }\n\n        public String generateDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"\\\"\").append(columnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(originalValue)).append(\"->\").append(convertDoubleQuotes(updateValue)).append(\"\\\"\").append(\",\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    @Data\n    public static class DataChangedRecord {\n\n        private String pkColumnName;\n        private Object pkColumnVal;\n        private List<DataColumnChangeResult> originalColumnDatas;\n        private List<DataColumnChangeResult> updatedColumns;\n\n        public boolean hasUpdate(Map<String, Object> columnNameValMap, Set<String> ignoredColumns, Set<String> ignoreAllColumns) {\n            if (originalColumnDatas == null) {\n                return true;\n            }\n            boolean hasUpdate = false;\n            updatedColumns = new ArrayList<>(originalColumnDatas.size());\n            for (DataColumnChangeResult originalColumn : originalColumnDatas) {\n                final String columnName = originalColumn.getColumnName().toUpperCase();\n                if (ignoredColumns != null && ignoredColumns.contains(columnName) || ignoreAllColumns.contains(columnName)) {\n                    continue;\n                }\n                Object updatedValue = columnNameValMap.get(columnName);\n                if (originalColumn.isDataChanged(updatedValue)) {\n                    hasUpdate = true;\n                    originalColumn.setUpdateValue(updatedValue);\n                    updatedColumns.add(originalColumn);\n                }\n            }\n            return hasUpdate;\n        }\n\n        public String generateUpdatedDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"{\");\n            if (pkColumnName != null) {\n                sb.append(\"\\\"\").append(pkColumnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(pkColumnVal)).append(\"\\\"\").append(\",\");\n            }\n            for (DataColumnChangeResult update : updatedColumns) {\n                sb.append(update.generateDataStr());\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"}\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    public static class DataUpdateLimitationException extends MybatisPlusException {\n\n        public DataUpdateLimitationException(String message) {\n            super(message);\n        }\n\n        public static DataUpdateLimitationException DEFAULT = new DataUpdateLimitationException(\"本次操作 因超过系统安全阈值 被拦截，如需继续，请联系管理员!\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.select.WithItem;\nimport net.sf.jsqlparser.statement.update.Update;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class DataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private DataPermissionHandler dataPermissionHandler;\n\n    @SuppressWarnings(\"RedundantThrows\")\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            // 参照 com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.processSelect 做的修改\n            final String whereSegment = (String) obj;\n            processSelectBody(select, whereSegment);\n            List<WithItem> withItemsList = select.getWithItemsList();\n            if (!CollectionUtils.isEmpty(withItemsList)) {\n                withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));\n            }\n        } else {\n            // 兼容原来的旧版 DataPermissionHandler 场景\n            if (select instanceof PlainSelect) {\n                this.setWhere((PlainSelect) select, (String) obj);\n            } else if (select instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) select;\n                List<Select> selectBodyList = setOperationList.getSelects();\n                selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));\n            }\n        }\n    }\n\n    /**\n     * 设置 where 条件\n     *\n     * @param plainSelect  查询对象\n     * @param whereSegment 查询条件片段\n     */\n    protected void setWhere(PlainSelect plainSelect, String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        // 兼容旧版的数据权限处理\n        final Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), whereSegment);\n        if (null != sqlSegment) {\n            plainSelect.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(update.getTable(), update.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            update.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(delete.getTable(), delete.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            delete.setWhere(sqlSegment);\n        }\n    }\n\n    protected Expression getUpdateOrDeleteExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            return andExpression(table, where, whereSegment);\n        } else {\n            // 兼容旧版的数据权限处理\n            return dataPermissionHandler.getSqlSegment(where, whereSegment);\n        }\n    }\n\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        // 只有新版数据权限处理器才会执行到这里\n        final MultiDataPermissionHandler handler = (MultiDataPermissionHandler) dataPermissionHandler;\n        return handler.getSqlSegment(table, where, whereSegment);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.DynamicTableNameHandler;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.UnsupportedStatement;\n\n/**\n * 动态表处理器 (基于JsqlParser解析器)\n * <p>默认情况下,如果JsqlParser解析不了,则调用父类的解析进行处理</p>\n *\n * @author nieqiurong\n * @see DynamicTableNameHandler\n * @since 3.5.11\n */\n@Setter\npublic class DynamicTableNameJsqlParserInnerInterceptor extends DynamicTableNameInnerInterceptor {\n\n    /**\n     * 是否忽略解析异常\n     */\n    private boolean ignoreException = false;\n\n    /**\n     * 当JsqlParser无法解析语句时是否进行调用父类继续解析处理\n     *\n     * @see com.baomidou.mybatisplus.core.toolkit.TableNameParser\n     */\n    private boolean shouldFallback = true;\n\n\n    @Deprecated\n    public DynamicTableNameJsqlParserInnerInterceptor() {\n    }\n\n    public DynamicTableNameJsqlParserInnerInterceptor(TableNameHandler tableNameHandler) {\n        super(tableNameHandler);\n    }\n\n    @Override\n    protected String processTableName(String sql) {\n        boolean unsupported = false;\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            statement.accept(new DynamicTableNameHandler(sql, super.getTableNameHandler()));\n            if (statement instanceof UnsupportedStatement) {\n                unsupported = true;\n                return super.processTableName(sql);\n            }\n            return statement.toString();\n        } catch (Exception exception) {\n            return handleFallback(unsupported, sql, exception);\n        }\n    }\n\n    private String handleFallback(boolean unsupported, String sql, Exception originalException) {\n        Exception exception = originalException;\n        if (!unsupported || shouldFallback) {\n            try {\n                return super.processTableName(sql);\n            } catch (Exception e) {\n                exception = e;\n            }\n        }\n        if (ignoreException) {\n            return sql;\n        }\n        throw new MybatisPlusException(\"Table name processing failed : \", exception);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/IllegalSQLInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.EncryptUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Function;\nimport net.sf.jsqlparser.expression.Parenthesis;\nimport net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.Join;\nimport net.sf.jsqlparser.statement.select.ParenthesedSelect;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 由于开发人员水平参差不齐，即使订了开发规范很多人也不遵守\n * <p>SQL是影响系统性能最重要的因素，所以拦截掉垃圾SQL语句</p>\n * <br>\n * <p>拦截SQL类型的场景</p>\n * <p>1.必须使用到索引，包含left join连接字段，符合索引最左原则</p>\n * <p>必须使用索引好处，</p>\n * <p>1.1 如果因为动态SQL，bug导致update的where条件没有带上，全表更新上万条数据</p>\n * <p>1.2 如果检查到使用了索引，SQL性能基本不会太差</p>\n * <br>\n * <p>2.SQL尽量单表执行，有查询left join的语句，必须在注释里面允许该SQL运行，否则会被拦截，有left join的语句，如果不能拆成单表执行的SQL，请leader商量在做</p>\n * <p>https://gaoxianglong.github.io/shark</p>\n * <p>SQL尽量单表执行的好处</p>\n * <p>2.1 查询条件简单、易于开理解和维护；</p>\n * <p>2.2 扩展性极强；（可为分库分表做准备）</p>\n * <p>2.3 缓存利用率高；</p>\n * <p>2.在字段上使用函数</p>\n * <br>\n * <p>3.where条件为空</p>\n * <p>4.where条件使用了 !=</p>\n * <p>5.where条件使用了 not 关键字</p>\n * <p>6.where条件使用了 or 关键字</p>\n * <p>7.where条件使用了 使用子查询</p>\n *\n * @author willenfoo\n * @deprecated 3.5.10 实用性不高,语法分析太差,计划移除\n * @since 3.4.0\n */\n@Deprecated\npublic class IllegalSQLInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 缓存验证结果，提高性能\n     */\n    private static final Set<String> cacheValidResult = new HashSet<>();\n    /**\n     * 缓存表的索引信息\n     */\n    private static final Map<String, List<IndexInfo>> indexInfoMap = new ConcurrentHashMap<>();\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpStatementHandler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || InterceptorIgnoreHelper.willIgnoreIllegalSql(ms.getId())) {\n            return;\n        }\n        BoundSql boundSql = mpStatementHandler.boundSql();\n        String originalSql = boundSql.getSql();\n        logger.debug(\"检查SQL是否合规，SQL:\" + originalSql);\n        String md5Base64 = EncryptUtils.md5Base64(originalSql);\n        if (cacheValidResult.contains(md5Base64)) {\n            logger.debug(\"该SQL已验证，无需再次验证，，SQL:\" + originalSql);\n            return;\n        }\n        parserSingle(originalSql, connection);\n        //缓存验证结果\n        cacheValidResult.add(md5Base64);\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (select instanceof PlainSelect) {\n            PlainSelect plainSelect = (PlainSelect) select;\n            FromItem fromItem = ((PlainSelect) select).getFromItem();\n            while (fromItem instanceof ParenthesedSelect) {\n                ParenthesedSelect parenthesedSelect = (ParenthesedSelect) fromItem;\n                plainSelect = (PlainSelect) parenthesedSelect.getSelect();\n                fromItem = plainSelect.getFromItem();\n            }\n            Expression where = plainSelect.getWhere();\n            Assert.notNull(where, \"非法SQL，必须要有where条件\");\n            Table table = (Table) plainSelect.getFromItem();\n            List<Join> joins = plainSelect.getJoins();\n            validWhere(where, table, (Connection) obj);\n            validJoins(joins, table, (Connection) obj);\n        }\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        Expression where = update.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = update.getTable();\n        List<Join> joins = update.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        Expression where = delete.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = delete.getTable();\n        List<Join> joins = delete.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    /**\n     * 验证expression对象是不是 or、not等等\n     *\n     * @param expression ignore\n     */\n    private void validExpression(Expression expression) {\n        while (expression instanceof Parenthesis) {\n            Parenthesis parenthesis = (Parenthesis) expression;\n            expression = parenthesis.getExpression();\n        }\n        //where条件使用了 or 关键字\n        if (expression instanceof OrExpression) {\n            OrExpression orExpression = (OrExpression) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【or】关键字，错误or信息：\" + orExpression.toString());\n        } else if (expression instanceof NotEqualsTo) {\n            NotEqualsTo notEqualsTo = (NotEqualsTo) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【!=】关键字，错误!=信息：\" + notEqualsTo.toString());\n        } else if (expression instanceof BinaryExpression) {\n            BinaryExpression binaryExpression = (BinaryExpression) expression;\n            // TODO 升级 jsqlparser 后待实现\n//            if (binaryExpression.isNot()) {\n//                throw new MybatisPlusException(\"非法SQL，where条件中不能使用【not】关键字，错误not信息：\" + binaryExpression.toString());\n//            }\n            if (binaryExpression.getLeftExpression() instanceof Function) {\n                Function function = (Function) binaryExpression.getLeftExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用数据库函数，错误函数信息：\" + function.toString());\n            }\n            if (binaryExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) binaryExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        } else if (expression instanceof InExpression) {\n            InExpression inExpression = (InExpression) expression;\n            if (inExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) inExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        }\n\n    }\n\n    /**\n     * 如果SQL用了 left Join，验证是否有or、not等等，并且验证是否使用了索引\n     *\n     * @param joins      ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validJoins(List<Join> joins, Table table, Connection connection) {\n        //允许执行join，验证jion是否使用索引等等\n        if (joins != null) {\n            for (Join join : joins) {\n                Table rightTable = (Table) join.getRightItem();\n                Collection<Expression> onExpressions = join.getOnExpressions();\n                for (Expression expression : onExpressions) {\n                    validWhere(expression, table, rightTable, connection);\n                }\n            }\n        }\n    }\n\n    /**\n     * 检查是否使用索引\n     *\n     * @param table      ignore\n     * @param columnName ignore\n     * @param connection ignore\n     */\n    private void validUseIndex(Table table, String columnName, Connection connection) {\n        //是否使用索引\n        boolean useIndexFlag = false;\n        if (StringUtils.isNotBlank(columnName)) {\n            String tableName = table.getName();\n            //表存在的索引\n            String dbName = getPartItemValue(table, 1);\n            String catalogName = getPartItemValue(table, 2);\n            columnName = SqlParserUtils.removeWrapperSymbol(columnName);\n            List<IndexInfo> indexInfos = getIndexInfos(catalogName, dbName, tableName, connection);\n            for (IndexInfo indexInfo : indexInfos) {\n                if (indexInfo.getColumnName().equalsIgnoreCase(columnName)) {\n                    useIndexFlag = true;\n                    break;\n                }\n            }\n        }\n        if (!useIndexFlag) {\n            throw new MybatisPlusException(\"非法SQL，SQL未使用到索引, table:\" + table.getName() + \", columnName:\" + columnName);\n        }\n    }\n\n    private String getPartItemValue(Table table, int index) {\n        return index < table.getNameParts().size() ? table.getNameParts().get(index) : null;\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Connection connection) {\n        validWhere(expression, table, null, connection);\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param joinTable  ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Table joinTable, Connection connection) {\n        validExpression(expression);\n        if (expression instanceof BinaryExpression) {\n            //获得左边表达式\n            Expression leftExpression = ((BinaryExpression) expression).getLeftExpression();\n            validExpression(leftExpression);\n\n            //如果左边表达式为Column对象，则直接获得列名\n            if (leftExpression instanceof Column) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                if (joinTable != null && rightExpression instanceof Column) {\n                    if (Objects.equals(((Column) rightExpression).getTable().getName(), table.getAlias().getName())) {\n                        validUseIndex(table, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(joinTable, ((Column) leftExpression).getColumnName(), connection);\n                    } else {\n                        validUseIndex(joinTable, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                    }\n                } else {\n                    //获得列名\n                    validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                }\n            }\n            //如果BinaryExpression，进行迭代\n            else if (leftExpression instanceof BinaryExpression) {\n                validWhere(leftExpression, table, joinTable, connection);\n            }\n\n            //获得右边表达式，并分解\n            if (joinTable != null) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                validExpression(rightExpression);\n            }\n        }\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param dbName    数据库名\n     * @param tableName 表名\n     * @param conn      数据库连接\n     * @return 索引信息\n     * @see #getIndexInfos(String, String, String, String, Connection)\n     * @deprecated 3.5.11\n     */\n    public List<IndexInfo> getIndexInfos(String dbName, String tableName, Connection conn) {\n        return getIndexInfos(null, dbName, tableName, conn);\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key       缓存key\n     * @param dbName    数据库名\n     * @param tableName 表名\n     * @param conn      数据库连接\n     * @return 索引信息\n     * @see #getIndexInfos(String, String, String, String, Connection)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public List<IndexInfo> getIndexInfos(String key, String dbName, String tableName, Connection conn) {\n        return getIndexInfos(key, null, dbName, tableName, conn);\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key         缓存key\n     * @param catalogName catalogName\n     * @param dbName      数据库名\n     * @param tableName   表名\n     * @param conn        数据库连接\n     * @return 索引信息\n     * @since 3.5.11\n     */\n    public List<IndexInfo> getIndexInfos(String key, String catalogName, String dbName, String tableName, Connection conn) {\n        List<IndexInfo> indexInfos = null;\n        if (StringUtils.isNotBlank(key)) {\n            indexInfos = indexInfoMap.get(key);\n        }\n        if (indexInfos == null || indexInfos.isEmpty()) {\n            ResultSet rs;\n            try {\n                DatabaseMetaData metadata = conn.getMetaData();\n                String catalog = StringUtils.isBlank(catalogName) ? conn.getCatalog() : catalogName;\n                String schema = StringUtils.isBlank(dbName) ? conn.getSchema() : dbName;\n                rs = metadata.getIndexInfo(catalog, schema, SqlParserUtils.removeWrapperSymbol(tableName), false, true);\n                indexInfos = new ArrayList<>();\n                while (rs.next()) {\n                    //索引中的列序列号等于1，才有效\n                    if (Objects.equals(rs.getString(8), \"1\")) {\n                        IndexInfo indexInfo = new IndexInfo();\n                        indexInfo.setDbName(rs.getString(1));\n                        indexInfo.setTableName(rs.getString(3));\n                        indexInfo.setColumnName(rs.getString(9));\n                        indexInfos.add(indexInfo);\n                    }\n                }\n                if (StringUtils.isNotBlank(key)) {\n                    indexInfoMap.put(key, indexInfos);\n                }\n            } catch (SQLException e) {\n                logger.error(String.format(\"getIndexInfo fault, with key:%s, dbName:%s, tableName:%s\", key, dbName, tableName), e);\n            }\n        }\n        return indexInfos;\n    }\n\n    /**\n     * 索引对象\n     */\n    @Data\n    private static class IndexInfo {\n\n        private String dbName;\n\n        private String tableName;\n\n        private String columnName;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/PaginationInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;\nimport com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\nimport org.apache.ibatis.cache.CacheKey;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\n/**\n * 分页拦截器\n * <p>\n * 默认对 left join 进行优化,虽然能优化count,但是加上分页的话如果1对多本身结果条数就是不正确的\n *\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@SuppressWarnings({\"rawtypes\"})\npublic class PaginationInnerInterceptor implements InnerInterceptor {\n    /**\n     * 获取jsqlparser中count的SelectItem\n     */\n    protected static final List<SelectItem<?>> COUNT_SELECT_ITEM = Collections.singletonList(\n        new SelectItem<>(new Column().withColumnName(\"COUNT(*)\")).withAlias(new Alias(\"total\"))\n    );\n    protected static final Map<String, MappedStatement> countMsCache = new ConcurrentHashMap<>();\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n\n    /**\n     * 溢出总页数后是否进行处理\n     */\n    protected boolean overflow;\n    /**\n     * 单页分页条数限制\n     */\n    protected Long maxLimit;\n    /**\n     * 数据库类型\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private DbType dbType;\n    /**\n     * 方言实现类\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private IDialect dialect;\n    /**\n     * 生成 countSql 优化掉 join\n     * 现在只支持 left join\n     *\n     * @since 3.4.2\n     */\n    protected boolean optimizeJoin = true;\n\n    public PaginationInnerInterceptor(DbType dbType) {\n        this.dbType = dbType;\n    }\n\n    public PaginationInnerInterceptor(IDialect dialect) {\n        this.dialect = dialect;\n    }\n\n    /**\n     * 这里进行count,如果count为0这返回false(就是不再执行sql了)\n     */\n    @Override\n    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {\n            return true;\n        }\n\n        BoundSql countSql;\n        MappedStatement countMs = buildCountMappedStatement(ms, page.countId());\n        if (countMs != null) {\n            countSql = countMs.getBoundSql(parameter);\n        } else {\n            countMs = buildAutoCountMappedStatement(ms);\n            String countSqlStr = autoCountSql(page, boundSql.getSql());\n            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n            countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);\n            PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());\n        }\n\n        CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);\n        List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);\n        long total = 0;\n        if (CollectionUtils.isNotEmpty(result)) {\n            // 个别数据库 count 没数据不会返回 0\n            Object o = result.get(0);\n            if (o != null) {\n                total = Long.parseLong(o.toString());\n            }\n        }\n        page.setTotal(total);\n        return continuePage(page);\n    }\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (null == page) {\n            return;\n        }\n\n        // 处理 orderBy 拼接\n        boolean addOrdered = false;\n        String buildSql = boundSql.getSql();\n        List<OrderItem> orders = page.orders();\n        if (CollectionUtils.isNotEmpty(orders)) {\n            addOrdered = true;\n            buildSql = this.concatOrderBy(buildSql, orders);\n        }\n\n        // size 小于 0 且不限制返回值则不构造分页sql\n        Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;\n        if (page.getSize() < 0 && null == _limit) {\n            if (addOrdered) {\n                PluginUtils.mpBoundSql(boundSql).sql(buildSql);\n            }\n            return;\n        }\n\n        handlerLimit(page, _limit);\n        IDialect dialect = findIDialect(executor);\n\n        final Configuration configuration = ms.getConfiguration();\n        DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n\n        List<ParameterMapping> mappings = mpBoundSql.parameterMappings();\n        Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();\n        model.consumers(mappings, configuration, additionalParameter);\n        mpBoundSql.sql(model.getDialectSql());\n        mpBoundSql.parameterMappings(mappings);\n    }\n\n    /**\n     * 获取分页方言类的逻辑\n     *\n     * @param executor Executor\n     * @return 分页方言类\n     */\n    protected IDialect findIDialect(Executor executor) {\n        if (dialect != null) {\n            return dialect;\n        }\n        if (dbType != null) {\n            dialect = DialectFactory.getDialect(dbType);\n            return dialect;\n        }\n        return DialectFactory.getDialect(JdbcUtils.getDbType(executor));\n    }\n\n    /**\n     * 获取指定的 id 的 MappedStatement\n     *\n     * @param ms      MappedStatement\n     * @param countId id\n     * @return MappedStatement\n     */\n    protected MappedStatement buildCountMappedStatement(MappedStatement ms, String countId) {\n        if (StringUtils.isNotBlank(countId)) {\n            final String id = ms.getId();\n            if (!countId.contains(StringPool.DOT)) {\n                countId = id.substring(0, id.lastIndexOf(StringPool.DOT) + 1) + countId;\n            }\n            final Configuration configuration = ms.getConfiguration();\n            try {\n                return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> configuration.getMappedStatement(key, false));\n            } catch (Exception e) {\n                logger.warn(String.format(\"can not find this countId: [\\\"%s\\\"]\", countId));\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 构建 mp 自用自动的 MappedStatement\n     *\n     * @param ms MappedStatement\n     * @return MappedStatement\n     */\n    protected MappedStatement buildAutoCountMappedStatement(MappedStatement ms) {\n        final String countId = ms.getId() + \"_mpCount\";\n        final Configuration configuration = ms.getConfiguration();\n        return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> {\n            MappedStatement.Builder builder = new MappedStatement.Builder(configuration, key, ms.getSqlSource(), ms.getSqlCommandType());\n            builder.resource(ms.getResource());\n            builder.fetchSize(ms.getFetchSize());\n            builder.statementType(ms.getStatementType());\n            builder.timeout(ms.getTimeout());\n            builder.parameterMap(ms.getParameterMap());\n            builder.resultMaps(Collections.singletonList(new ResultMap.Builder(configuration, Constants.MYBATIS_PLUS, Long.class, Collections.emptyList()).build()));\n            builder.resultSetType(ms.getResultSetType());\n            builder.cache(ms.getCache());\n            builder.flushCacheRequired(ms.isFlushCacheRequired());\n            builder.useCache(ms.isUseCache());\n            return builder.build();\n        });\n    }\n\n    /**\n     * 获取自动优化的 countSql\n     *\n     * @param page 参数\n     * @param sql  sql\n     * @return countSql\n     */\n    public String autoCountSql(IPage<?> page, String sql) {\n        if (!page.optimizeCountSql()) {\n            return lowLevelCountSql(sql);\n        }\n        try {\n            Select select = (Select) JsqlParserGlobal.parse(sql);\n            // https://github.com/baomidou/mybatis-plus/issues/3920  分页增加union语法支持\n            if (select instanceof SetOperationList) {\n                return lowLevelCountSql(sql);\n            }\n            PlainSelect plainSelect = (PlainSelect) select;\n\n            // 优化 order by 在非分组情况下\n            List<OrderByElement> orderBy = plainSelect.getOrderByElements();\n            if (CollectionUtils.isNotEmpty(orderBy)) {\n                boolean canClean = true;\n                for (OrderByElement order : orderBy) {\n                    // order by 里带参数,不去除order by\n                    Expression expression = order.getExpression();\n                    if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {\n                        canClean = false;\n                        break;\n                    }\n                }\n                if (canClean) {\n                    plainSelect.setOrderByElements(null);\n                }\n            }\n            Distinct distinct = plainSelect.getDistinct();\n            GroupByElement groupBy = plainSelect.getGroupBy();\n            // 包含 distinct、groupBy 不优化\n            if (null != distinct || null != groupBy) {\n                return lowLevelCountSql(select.toString());\n            }\n            //#95 Github, selectItems contains #{} ${}, which will be translated to ?, and it may be in a function: power(#{myInt},2)\n            for (SelectItem item : plainSelect.getSelectItems()) {\n                if (item.toString().contains(StringPool.QUESTION_MARK)) {\n                    return lowLevelCountSql(select.toString());\n                }\n            }\n\n            // 包含 join 连表,进行判断是否移除 join 连表\n            if (optimizeJoin && page.optimizeJoinOfCountSql()) {\n                List<Join> joins = plainSelect.getJoins();\n                if (CollectionUtils.isNotEmpty(joins)) {\n                    boolean canRemoveJoin = true;\n                    String whereS = Optional.ofNullable(plainSelect.getWhere()).map(Expression::toString).orElse(StringPool.EMPTY);\n                    // 不区分大小写\n                    whereS = whereS.toLowerCase();\n                    for (Join join : joins) {\n                        if (!join.isLeft()) {\n                            canRemoveJoin = false;\n                            break;\n                        }\n                        FromItem rightItem = join.getRightItem();\n                        String str = \"\";\n                        if (rightItem instanceof Table) {\n                            Table table = (Table) rightItem;\n                            str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;\n                        } else if (rightItem instanceof ParenthesedSelect) {\n                            ParenthesedSelect subSelect = (ParenthesedSelect) rightItem;\n                            /* 如果 left join 是子查询，并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {\n                                canRemoveJoin = false;\n                                break;\n                            }\n                            str = subSelect.getAlias().getName() + StringPool.DOT;\n                        }\n                        // 不区分大小写\n                        str = str.toLowerCase();\n\n                        if (whereS.contains(str)) {\n                            /* 如果 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            canRemoveJoin = false;\n                            break;\n                        }\n\n                        for (Expression expression : join.getOnExpressions()) {\n                            if (expression.toString().contains(StringPool.QUESTION_MARK)) {\n                                /* 如果 join 里包含 ?(代表有入参) 就不移除 join */\n                                canRemoveJoin = false;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (canRemoveJoin) {\n                        plainSelect.setJoins(null);\n                    }\n                }\n            }\n\n            // 优化 SQL\n            plainSelect.setSelectItems(COUNT_SELECT_ITEM);\n            return select.toString();\n        } catch (JSQLParserException e) {\n            // 无法优化使用原 SQL\n            logger.warn(\"optimize this sql to a count sql has exception, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"optimize this sql to a count sql has error, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e);\n        }\n        return lowLevelCountSql(sql);\n    }\n\n    /**\n     * 无法进行count优化时,降级使用此方法\n     *\n     * @param originalSql 原始sql\n     * @return countSql\n     */\n    protected String lowLevelCountSql(String originalSql) {\n        return SqlParserUtils.getOriginalCountSql(originalSql);\n    }\n\n    /**\n     * 查询SQL拼接Order By\n     *\n     * @param originalSql 需要拼接的SQL\n     * @return ignore\n     */\n    public String concatOrderBy(String originalSql, List<OrderItem> orderList) {\n        try {\n            Select selectBody = (Select) JsqlParserGlobal.parse(originalSql);\n            if (selectBody instanceof PlainSelect) {\n                PlainSelect plainSelect = (PlainSelect) selectBody;\n                List<OrderByElement> orderByElements = plainSelect.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                plainSelect.setOrderByElements(orderByElementsReturn);\n                return plainSelect.toString();\n            } else if (selectBody instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) selectBody;\n                List<OrderByElement> orderByElements = setOperationList.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                setOperationList.setOrderByElements(orderByElementsReturn);\n                return setOperationList.toString();\n            } else if (selectBody instanceof WithItem) {\n                // todo: don't known how to resole\n                return originalSql;\n            } else {\n                return originalSql;\n            }\n        } catch (JSQLParserException e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e);\n        }\n        return originalSql;\n    }\n\n    protected List<OrderByElement> addOrderByElements(List<OrderItem> orderList, List<OrderByElement> orderByElements) {\n        List<OrderByElement> additionalOrderBy = orderList.stream()\n            .filter(item -> StringUtils.isNotBlank(item.getColumn()))\n            .map(item -> {\n                OrderByElement element = new OrderByElement();\n                element.setExpression(new Column(item.getColumn()));\n                element.setAsc(item.isAsc());\n                element.setAscDescPresent(true);\n                return element;\n            }).collect(Collectors.toList());\n        if (CollectionUtils.isEmpty(orderByElements)) {\n            return additionalOrderBy;\n        }\n        // github pull/3550 优化排序，比如：默认 order by id 前端传了name排序，设置为 order by name,id\n        additionalOrderBy.addAll(orderByElements);\n        return additionalOrderBy;\n    }\n\n    /**\n     * count 查询之后,是否继续执行分页\n     *\n     * @param page 分页对象\n     * @return 是否\n     */\n    protected boolean continuePage(IPage<?> page) {\n        if (page.getTotal() <= 0) {\n            return false;\n        }\n        if (page.getCurrent() > page.getPages()) {\n            if (overflow) {\n                //溢出总页数处理\n                handlerOverflow(page);\n            } else {\n                // 超过最大范围，未设置溢出逻辑中断 list 执行\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 处理超出分页条数限制,默认归为限制数\n     *\n     * @param page IPage\n     */\n    protected void handlerLimit(IPage<?> page, Long limit) {\n        final long size = page.getSize();\n        if (limit != null && limit > 0 && (size > limit || size < 0)) {\n            page.setSize(limit);\n        }\n    }\n\n    /**\n     * 处理页数溢出,默认设置为第一页\n     *\n     * @param page IPage\n     */\n    protected void handlerOverflow(IPage<?> page) {\n        page.setCurrent(1);\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties)\n            .whenNotBlank(\"overflow\", Boolean::parseBoolean, this::setOverflow)\n            .whenNotBlank(\"dbType\", DbType::getDbType, this::setDbType)\n            .whenNotBlank(\"dialect\", ClassUtils::newInstance, this::setDialect)\n            .whenNotBlank(\"maxLimit\", Long::parseLong, this::setMaxLimit)\n            .whenNotBlank(\"optimizeJoin\", Boolean::parseBoolean, this::setOptimizeJoin);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport lombok.*;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Parenthesis;\nimport net.sf.jsqlparser.expression.RowConstructor;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.*;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class TenantLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private TenantLineHandler tenantLineHandler;\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), null));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), null));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        final String whereSegment = (String) obj;\n        processSelectBody(select, whereSegment);\n        List<WithItem> withItemsList = select.getWithItemsList();\n        if (!CollectionUtils.isEmpty(withItemsList)) {\n            withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));\n        }\n    }\n\n    @Override\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(insert.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<Column> columns = insert.getColumns();\n        if (CollectionUtils.isEmpty(columns)) {\n            // 针对不给列名的insert 不处理\n            return;\n        }\n        String tenantIdColumn = tenantLineHandler.getTenantIdColumn();\n        if (tenantLineHandler.ignoreInsert(columns, tenantIdColumn)) {\n            // 针对已给出租户列的insert 不处理\n            return;\n        }\n        columns.add(new Column(tenantIdColumn));\n        Expression tenantId = tenantLineHandler.getTenantId();\n        // fixed gitee pulls/141 duplicate update\n        List<UpdateSet> duplicateUpdateColumns = insert.getDuplicateUpdateSets();\n        if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {\n            duplicateUpdateColumns.add(new UpdateSet(new Column(tenantIdColumn), tenantId));\n        }\n\n        Select select = insert.getSelect();\n        if (select instanceof PlainSelect) { //fix github issue 4998  修复升级到4.5版本的问题\n            this.processInsertSelect(select, (String) obj);\n        } else if (insert.getValues() != null) {\n            // fixed github pull/295\n            Values values = insert.getValues();\n            ExpressionList<Expression> expressions = (ExpressionList<Expression>) values.getExpressions();\n            if (expressions instanceof ParenthesedExpressionList) {\n                expressions.addExpression(tenantId);\n            } else {\n                if (CollectionUtils.isNotEmpty(expressions)) {//fix github issue 4998 jsqlparse 4.5 批量insert ItemsList不是MultiExpressionList 了，需要特殊处理\n                    int len = expressions.size();\n                    for (int i = 0; i < len; i++) {\n                        Expression expression = expressions.get(i);\n                        if (expression instanceof Parenthesis) {\n                            ExpressionList rowConstructor = new RowConstructor<>()\n                                .withExpressions(new ExpressionList<>(((Parenthesis) expression).getExpression(), tenantId));\n                            expressions.set(i, rowConstructor);\n                        } else if (expression instanceof ParenthesedExpressionList) {\n                            ((ParenthesedExpressionList) expression).addExpression(tenantId);\n                        } else {\n                            expressions.add(tenantId);\n                        }\n                    }\n                } else {\n                    expressions.add(tenantId);\n                }\n            }\n        } else {\n            throw ExceptionUtils.mpe(\"Failed to process multiple-table update, please exclude the tableName or statementId\");\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Table table = update.getTable();\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<UpdateSet> sets = update.getUpdateSets();\n        if (!CollectionUtils.isEmpty(sets)) {\n            sets.forEach(us -> us.getValues().forEach(ex -> {\n                if (ex instanceof Select) {\n                    processSelectBody(((Select) ex), (String) obj);\n                }\n            }));\n        }\n        update.setWhere(this.andExpression(table, update.getWhere(), (String) obj));\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(delete.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere(), (String) obj));\n    }\n\n    /**\n     * 处理 insert into select\n     * <p>\n     * 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了\n     *\n     * @param selectBody SelectBody\n     */\n    protected void processInsertSelect(Select selectBody, final String whereSegment) {\n        if(selectBody instanceof PlainSelect){\n            PlainSelect plainSelect = (PlainSelect) selectBody;\n            FromItem fromItem = plainSelect.getFromItem();\n            if (fromItem instanceof Table) {\n                // fixed gitee pulls/141 duplicate update\n                processPlainSelect(plainSelect, whereSegment);\n                appendSelectItem(plainSelect.getSelectItems());\n            } else if (fromItem instanceof Select) {\n                Select subSelect = (Select) fromItem;\n                appendSelectItem(plainSelect.getSelectItems());\n                processInsertSelect(subSelect, whereSegment);\n            }\n        } else if(selectBody instanceof ParenthesedSelect){\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processInsertSelect(parenthesedSelect.getSelect(), whereSegment);\n\n        }\n    }\n\n    /**\n     * 追加 SelectItem\n     *\n     * @param selectItems SelectItem\n     */\n    protected void appendSelectItem(List<SelectItem<?>> selectItems) {\n        if (CollectionUtils.isEmpty(selectItems)) {\n            return;\n        }\n        if (selectItems.size() == 1) {\n            SelectItem item = selectItems.get(0);\n            Expression expression = item.getExpression();\n            if (expression instanceof AllColumns) {\n                return;\n            }\n        }\n        selectItems.add(new SelectItem<>(new Column(tenantLineHandler.getTenantIdColumn())));\n    }\n\n    /**\n     * 租户字段别名设置\n     * <p>tenantId 或 tableAlias.tenantId</p>\n     *\n     * @param table 表对象\n     * @return 字段\n     */\n    protected Column getAliasColumn(Table table) {\n        StringBuilder column = new StringBuilder();\n        // todo 该起别名就要起别名,禁止修改此处逻辑\n        if (table.getAlias() != null) {\n            column.append(table.getAlias().getName()).append(StringPool.DOT);\n        }\n        column.append(tenantLineHandler.getTenantIdColumn());\n        return new Column(column.toString());\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties).whenNotBlank(\"tenantLineHandler\",\n            ClassUtils::newInstance, this::setTenantLineHandler);\n    }\n\n    /**\n     * 构建租户条件表达式\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径（在原租户拦截器功能中，这个参数并不需要参与相关判断）\n     * @return 租户条件表达式\n     * @see BaseMultiTableInnerInterceptor#buildTableExpression(Table, Expression, String)\n     */\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            return null;\n        }\n        return new EqualsTo(getAliasColumn(table), tenantLineHandler.getTenantId());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/JSqlParserTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL 解析测试\n */\nclass JSqlParserTest {\n\n    @Test\n    void parser() throws Exception {\n        Select select = (Select) CCJSqlParserUtil.parse(\"SELECT a,b,c FROM tableName t WHERE t.col = 9 and b=c LIMIT 3, ?\");\n\n        PlainSelect ps = (PlainSelect) select;\n\n        System.out.println(ps.getWhere().toString());\n        System.out.println(ps.getSelectItems().get(1).toString());\n\n        AndExpression e = (AndExpression) ps.getWhere();\n        System.out.println(e.getLeftExpression());\n    }\n\n    @Test\n    void testDecr() throws JSQLParserException {\n        // 如果连一起 SqlParser 将无法解析 , 还有种处理方式就自减为负数的时候 转为 自增.\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a --110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a - -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a - -110\", parse2.toString());\n    }\n\n    @Test\n    void testIncr() throws JSQLParserException {\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a +-110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a + -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse2.toString());\n    }\n\n    @Test\n    void notLikeParser() throws Exception {\n        final String targetSql = \"SELECT * FROM tableName WHERE id NOT LIKE ?\";\n        Select select = (Select) CCJSqlParserUtil.parse(targetSql);\n        assertThat(select.toString()).isEqualTo(targetSql);\n    }\n\n    @Test\n    void updateWhereParser() throws Exception {\n        Update update = (Update) CCJSqlParserUtil.parse(\"Update tableName t SET t.a=(select c from tn where tn.id=t.id),b=2,c=3 \");\n        Assertions.assertNull(update.getWhere());\n    }\n\n    @Test\n    void deleteWhereParser() throws Exception {\n        Delete delete = (Delete) CCJSqlParserUtil.parse(\"delete from tableName t\");\n        Assertions.assertNull(delete.getWhere());\n    }\n\n//    @Test\n//    void testSelectForUpdate() throws Exception {\n//        Assertions.assertEquals(\"SELECT * FROM t_demo WHERE a = 1 FOR UPDATE\",\n//            CCJSqlParserUtil.parse(\"select * from t_demo where a = 1 for update\").toString());\n//        Assertions.assertEquals(\"SELECT * FROM sys_sms_send_record WHERE check_status = 0 ORDER BY submit_time ASC LIMIT 10 FOR UPDATE\",\n//            CCJSqlParserUtil.parse(\"select * from sys_sms_send_record where check_status = 0 for update order by submit_time asc limit 10\").toString());\n//    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/parser/JsqlParserSimpleSerialTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.FstFactory;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2023-08-03\n */\nclass JsqlParserSimpleSerialTest {\n    private final static int len = 1000;\n    private final static String sql = \"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\";\n\n    @Test\n    @EnabledOnJre(JRE.JAVA_8)\n    void test() throws JSQLParserException {\n        System.out.println(\"循环次数: \" + len);\n        noSerial();\n        jdkSerial();\n        fstSerial();\n    }\n\n    void noSerial() throws JSQLParserException {\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            CCJSqlParserUtil.parse(sql);\n        }\n        long endTime = System.currentTimeMillis();\n        long e1 = endTime - startTime;\n        System.out.printf(\"普通解析执行耗时: %s 毫秒, 均耗时: %s%n\", e1, (double) e1 / len);\n    }\n\n    void jdkSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = SerializationUtils.serialize(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"jdk serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) SerializationUtils.deserialize(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"jdk deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n\n    void fstSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        FstFactory factory = FstFactory.getDefaultFactory();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = factory.asByteArray(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"fst serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) factory.asObject(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"fst deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/parser/cache/FstFactoryTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser.cache;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfo;\nimport io.github.classgraph.ScanResult;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2023-08-06\n */\nclass FstFactoryTest {\n\n    @Test\n    void clazz() {\n        List<ClassInfo> list = new ArrayList<>();\n        List<ClassInfo> absList = new ArrayList<>();\n        try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(\"net.sf.jsqlparser\").scan()) {\n            for (ClassInfo classInfo : scanResult.getAllClasses()) {\n                if (!classInfo.isInterface() && classInfo.implementsInterface(Serializable.class)) {\n                    if (classInfo.isAbstract()) {\n                        absList.add(classInfo);\n                        continue;\n                    }\n                    list.add(classInfo);\n                }\n            }\n        }\n        list.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n        System.out.println(\"↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓\");\n        absList.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/MybatisPlusInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-30\n */\nclass MybatisPlusInterceptorTest {\n\n    @Test\n    void setProperties() {\n        Properties properties = new Properties();\n        properties.setProperty(\"@page\", \"com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor\");\n        properties.setProperty(\"page:maxLimit\", \"10\");\n        properties.setProperty(\"page:dbType\", \"h2\");\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.setProperties(properties);\n        List<InnerInterceptor> interceptors = interceptor.getInterceptors();\n\n        assertThat(interceptors).isNotEmpty();\n        assertThat(interceptors.size()).isEqualTo(1);\n\n        InnerInterceptor page = interceptors.getFirst();\n        assertThat(page).isInstanceOf(PaginationInnerInterceptor.class);\n\n        PaginationInnerInterceptor pii = (PaginationInnerInterceptor) page;\n        assertThat(pii.getMaxLimit()).isEqualTo(10);\n        assertThat(pii.getDbType()).isEqualTo(DbType.H2);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/BlockAttackInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-08-18\n */\nclass BlockAttackInnerInterceptorTest {\n\n    private final BlockAttackInnerInterceptor interceptor = new BlockAttackInnerInterceptor();\n\n    @Test\n    void update() {\n        checkEx(\"update user set name = null\", \"null where\");\n        checkEx(\"update user set name = null where 1=1\", \"1=1\");\n        checkEx(\"update user set name = null where 1<>2\", \"1<>2\");\n        checkEx(\"update user set name = null where 1!=2\", \"1!=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n        checkEx(\"update user set name = null where 1=1 and (2=2)\", \"1=1 and (2=2)\");\n        checkEx(\"update user set name = null where (1=1 and 2=2)\", \"(1=1 and 2=2)\");\n        checkEx(\"update user set name = null where (1=1 and (2=3 or 3=3))\", \"(1=1 and (2=3 or 3=3))\");\n\n        checkNotEx(\"update user set name = null where 1=?\", \"1=?\");\n    }\n\n    @Test\n    void delete() {\n        checkEx(\"delete from user\", \"null where\");\n        checkEx(\"delete from user where 1=1\", \"1=1\");\n        checkEx(\"delete from user where 1<>2\", \"1<>2\");\n        checkEx(\"delete from user where 1!=2\", \"1!=2\");\n        checkEx(\"delete from user where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"delete from user where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n    }\n\n    void checkEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNotNull();\n        assertThat(e).as(as).isInstanceOf(MybatisPlusException.class);\n    }\n\n    void checkNotEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNull();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataChangeRecorderInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass DataChangeRecorderInnerInterceptorTest {\n\n    private final DataChangeRecorderInnerInterceptor interceptor = new DataChangeRecorderInnerInterceptor();\n\n    @BeforeEach\n    public void initProperties() {\n        Properties properties = new Properties();\n        properties.put(\"ignoredTableColumns\", \"table_name1.column1,column2; h2user.*; *.column1,COLUMN2\");\n        interceptor.setProperties(properties);\n    }\n    @Test\n    void setProperties() throws Exception {\n        final Object ignoreAllColumns = getFieldValue(interceptor, \"ignoreAllColumns\");\n        Assertions.assertEquals(Set.of(\"COLUMN1\", \"COLUMN2\"), ignoreAllColumns);\n        final Object ignoredTableColumns = getFieldValue(interceptor, \"ignoredTableColumns\");\n        Assertions.assertEquals(Map.of(\"H2USER\", Set.of(\"*\"), \"TABLE_NAME1\", Set.of(\"COLUMN1\", \"COLUMN2\")), ignoredTableColumns);\n    }\n\n    private Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {\n        final Field field = DataChangeRecorderInnerInterceptor.class.getDeclaredField(fieldName);\n        field.setAccessible(true);\n        return  field.get(obj);\n    }\n\n    @Test\n    void processInsert() {\n        final Insert insert = new Insert();\n        insert.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processInsert(insert, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void processUpdate() {\n        final Update update = new Update();\n        update.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processUpdate(update, null, null, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void isDataChangedTest() {\n        var columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        Assertions.assertFalse(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new Object());\n        Assertions.assertTrue(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new BigDecimal(\"0\"));\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertTrue(columnChangeResult.isDataChanged(\"0\"));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(0));\n\n        Assertions.assertFalse(columnChangeResult.isDataChanged(new BigDecimal(\"0\") {}));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(new BigDecimal(\"1\") {}));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * 数据权限拦截器测试\n *\n * @author hubin\n * @since 3.4.1 +\n */\n@Slf4j\npublic class DataPermissionInterceptorTest {\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final Map<String, String> SQL_SEGMENT_MAP = new HashMap<>() {\n        {\n            put(TEST_1, \"username='123' or userId IN (1,2,3)\");\n            put(TEST_2, \"u.state=1 and u.amount > 1000\");\n            put(TEST_3, \"companyId in (1,2,3)\");\n            put(TEST_4, \"username like 'abc%'\");\n            put(TEST_5, \"id=1 and role_id in (select id from sys_role)\");\n        }\n    };\n\n    private static final DataPermissionInterceptor INTERCEPTOR = new DataPermissionInterceptor((where, mappedStatementId) -> {\n        try {\n            String sqlSegment = SQL_SEGMENT_MAP.get(mappedStatementId);\n            Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n            if (null != where) {\n                System.out.println(\"原 where : \" + where);\n                if (mappedStatementId.equals(TEST_4)) {\n                    // 这里测试返回 OR 条件\n                    return new OrExpression(where, sqlSegmentExpression);\n                }\n                return new AndExpression(where, sqlSegmentExpression);\n            }\n            return sqlSegmentExpression;\n        } catch (JSQLParserException e) {\n            log.error(\"解析错误:\", e);\n        }\n        return null;\n    });\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 OR username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(INTERCEPTOR.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * 动态表名内部拦截器测试\n *\n * @author miemie, hcl\n * @since 2020-07-16\n */\nclass DynamicTableNameInnerInterceptorTest {\n\n    /**\n     * 测试 SQL 中的动态表名替换\n     */\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void doIt() {\n        DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();\n        interceptor.setTableNameHandler((sql, tableName) -> tableName + \"_r\");\n\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM /**/t_user_r/* t_user */\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t left join entity_r e on e.id = t.id\", interceptor.changeTable(origin));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameJsqlParserInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author nieqiurong\n */\nclass DynamicTableNameJsqlParserInnerInterceptorTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicTableNameJsqlParserInnerInterceptor.class);\n\n    private static final DynamicTableNameJsqlParserInnerInterceptor interceptor;\n\n    static {\n        interceptor = new DynamicTableNameJsqlParserInnerInterceptor((sql, tableName) -> {\n            LOGGER.info(\"process table : {}\", tableName);\n            if (tableName.endsWith(\"`\") || tableName.endsWith(\"]\")) {\n                char first = tableName.charAt(0);\n                char last = tableName.charAt(tableName.length()-1);\n                return first + SqlParserUtils.removeWrapperSymbol(tableName) + \"_r\" + last;\n            }\n            return tableName + \"_r\";\n        });\n        interceptor.setShouldFallback(true);\n    }\n\n\n    private static final String SQL_SELECT_SUB_QUERY = \"SELECT /*+ materialize*/ strategy_id\"\n        + \"FROM\"\n        + \" ( SELECT  strat.cf_strategy_id \"\n        + \"   FROM strategy strt,\"\n        + \"        doc_sect_ver prodGrp\"\n        + \"  WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"           AND strat.module_type   IN ('sdfdsf','assdf')\"\n        + \")\";\n\n\n    private static final String SQL_SELECT_THREE_JOIN_WITH_ALIASE = \"select c.name, s.name, s.id, r.result\"\n        + \" from colleges c \"\n        + \" join students s\"\n        + \"   on c.id = s.college_id\"\n        + \" join results r\"\n        + \"   on s.id = r.student_id\"\n        + \"where c.id = 3\"\n        + \"  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\";\n\n    private static final String SQL_COMPLEX_ONE = \"INSERT INTO static_product\"\n        + \"  (\"\n        + \"   DISCOUNT_ID,\"\n        + \"    CATEGORY_ID,\"\n        + \"    PRODUCT_ID\"\n        + \"   )\"\n        + \"  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,\"\n        + \"     ALLNDC11.PRODUCT_ID,\"\n        + \"     ALLNDC11.NDC11\"\n        + \"  FROM ITEM ITEM\"\n        + \" INNER JOIN\"\n        + \"   (SELECT NODE.SOURCE_ID NDC11,\"\n        + \"    PR.PRODUCT_ID,\"\n        + \"     BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"        DR_BD_PRODUCT PR,\"\n        + \"       map_edge_ver node\"\n        + \"     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"    AND B.BUNDLE_STATUS         =3\"\n        + \"    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"     AND NODE.EDGE_TYPE          = 1\"\n        + \"      START WITH\"\n        + \"      (\"\n        + \"        NODE.DEST_ID              = PR.PRODUCT_ID\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"      AND B.BUNDLE_STATUS         =3\"\n        + \"      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"      AND NODE.EDGE_TYPE          = 1\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"     )\"\n        + \"       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID\"\n        + \"    AND PRIOR NODE.EDGE_TYPE           = 1\"\n        + \"    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)\"\n        + \"    ) ALLNDC11\"\n        + \"  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)\"\n        + \"  UNION\"\n        + \"   ( SELECT BD1.BUNDLE_DISCOUNT_ID,\"\n        + \"      PR.PRODUCT_ID,\"\n        + \"     ITEM.CAT_MAP_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"     DR_BD_PRODUCT PR,\"\n        + \"    ITEM ITEM\"\n        + \"    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID\"\n        + \"   AND B.BUNDLE_STATUS         =3\"\n        + \"   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"   AND item.cat_map_id         =pr.product_id\"\n        + \"    )\";\n\n    private static final String SQL_MERGE_COMPLEX = \"MERGE INTO  cf_procedure proc USING\"\n        + \" (\"\n        + \" WITH NON_STRATEGY_DETAILS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_strategy_id\"\n        + \"    FROM\"\n        + \"     ( SELECT  strat.cf_strategy_id\"\n        + \"        FROM cf_strategy strat,\"\n        + \"             struct_doc_Sect_ver prodGrp\"\n        + \"        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"                 AND strat.src_mgr_id     = prodGrp.mgr_id\"\n        + \"                 AND strat.src_ver_num    = prodGrp.ver_num\"\n        + \"                 AND strat.module_type   IN ('COMPL','PRCMSTR')\"\n        + \"   )  ),\"\n        + \"   NON_STRATEGY_COMPS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_component_id\"\n        + \"   FROM\"\n        + \"   (\"\n        + \"     SELECT comp.cf_component_id AS cf_component_id\"\n        + \"     FROM   cf_component comp,\"\n        + \"            tier_basis_ver tb\"\n        + \"     WHERE  comp.bucket_src_id   = tb.tier_basis_id\"\n        + \"             AND comp.bucket_src_mgr_id  = tb.mgr_id\"\n        + \"             AND comp.bucket_src_ver_num = tb.ver_num\"\n        + \"             AND comp.module_type       IN ('COMPL','PRCMSTR')\"\n        + \"   )\"\n        + \"   ) ,\"\n        + \" NON_STRAT_PERIODS AS (\"\n        + \"   SELECT /*+ materialize*/ cf_period_id\"\n        + \"   FROM\"\n        + \"         cf_period per,\"\n        + \"         struct_doc_sect_ver prodGrp\"\n        + \"   WHERE  per.src_id            = prodGrp.struct_doc_sect_id\"\n        + \"         AND per.src_mgr_id     = prodGrp.mgr_id\"\n        + \"         AND per.src_ver_num    = prodGrp.ver_num\"\n        + \"         AND per.module_type    IN ('COMPL','PRCMSTR')\"\n        + \"         AND per.pmt_status NOT IN ('TERM','REV')\"\n\n        + \"    SELECT DISTINCT cf_procedure_id\"\n        + \"   FROM\"\n        + \"     (SELECT /*+ LEADING(comp,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  non_strategy_comps comp,\"\n        + \"           cf_procedure proc\"\n        + \"     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND comp.cf_component_id    = proc.cf_component_id\"\n        + \"    UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  cf_procedure proc,\"\n        + \"           non_strategy_details strat\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND strat.cf_strategy_id = proc.cf_strategy_id\"\n        + \"     UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"          proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM cf_procedure proc,\"\n        + \"          non_strat_periods periods\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND periods.CF_PERIOD_ID = proc.period_id\"\n        + \"     )\"\n        + \"      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id)\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET proc.variable_name = 'TierResultSSName';\";\n\n    private static final String SQL_MERGE_COMPLEX_TWO = \" MERGE INTO cf_procedure_ver procVer USING\"\n        + \"   (SELECT cf_procedure_id\"\n        + \"    FROM cf_procedure proc\"\n        + \"    WHERE proc.variable_name                  = 'TierResultSSName'\"\n        + \"   ) proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id )\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET procVer.variable_name = 'TierResultSSName'\"\n        + \"   WHERE procVer.variable_name <> 'TierResultSSName';\";\n\n    @Test\n    public void testSelectOneTable() {\n        var sql = \"SELECT name, age FROM table1 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTables() {\n        var sql = \"SELECT name, age FROM table1,table2\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTables() {\n        var sql = \"SELECT name, age FROM table1,table2,table3 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r, table3_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneTableWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliaseAndNoCondition() {\n        var sql = \"select xx from table1 a,table2 b\";\n        assertEquals(\"SELECT xx FROM table1_r a, table2_r b\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2, table3 t3 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2, table3_r t3 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectWithSubQuery() {\n        assertEquals(\"SELECT /*+ materialize */ strategy_idFROM(SELECT strat.cf_strategy_id FROM strategy_r strt, doc_sect_ver_r prodGrp WHERE strat.src_id = prodGrp.struct_doc_sect_id AND strat.module_type IN ('sdfdsf', 'assdf'))\", interceptor.changeTable(SQL_SELECT_SUB_QUERY));\n    }\n\n    @Test\n    public void testSelectWithOneJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneJoinWithAliase() {\n        var sql = \"SELECT coluname(s) FROM table1 t1 join table2 t2 ON t1.coluname=t2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r t1 JOIN table2_r t2 ON t1.coluname = t2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneLeftJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 left outer join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r LEFT OUTER JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testShouldIgnoreDual() {\n        var sql = \"select * from dual\";\n        assertEquals(\"SELECT * FROM dual_r\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectTwoJoinWithAliase() {\n        assertEquals(\"select c.name, s.name, s.id, r.result from colleges_r c  join students_r s   on c.id = s.college_id join results_r r   on s.id = r.student_idwhere c.id = 3  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\", interceptor.changeTable(SQL_SELECT_THREE_JOIN_WITH_ALIASE));\n    }\n\n    @Test\n    public void testInsertWithValues() {\n        var sql = \"INSERT INTO table_name VALUES (value1,value2,value3,...)\";\n        assertEquals(\"INSERT INTO table_name_r VALUES (value1,value2,value3,...)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertComplex() {\n        assertEquals(\"INSERT INTO static_product_r  (   DISCOUNT_ID,    CATEGORY_ID,    PRODUCT_ID   )  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,     ALLNDC11.PRODUCT_ID,     ALLNDC11.NDC11  FROM ITEM_r ITEM INNER JOIN   (SELECT NODE.SOURCE_ID NDC11,    PR.PRODUCT_ID,     BD1.BUNDLE_DISCOUNT_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,        DR_BD_PRODUCT_r PR,       map_edge_ver_r node     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE      AND B.BUNDLE_ID             =BD1.BUNDLE_ID    AND B.BUNDLE_STATUS         =3    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1     AND NODE.EDGE_TYPE          = 1      START WITH      (        NODE.DEST_ID              = PR.PRODUCT_ID      AND B.BUNDLE_ID             =BD1.BUNDLE_ID      AND B.BUNDLE_STATUS         =3      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1      AND NODE.EDGE_TYPE          = 1      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE     )       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID    AND PRIOR NODE.EDGE_TYPE           = 1    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)    ) ALLNDC11  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)  UNION   ( SELECT BD1.BUNDLE_DISCOUNT_ID,      PR.PRODUCT_ID,     ITEM.CAT_MAP_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,     DR_BD_PRODUCT_r PR,    ITEM_r ITEM    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID   AND B.BUNDLE_STATUS         =3   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID    AND BD1.IS_DYNAMIC_CATEGORY!= 1   AND item.cat_map_id         =pr.product_id    )\", interceptor.changeTable(SQL_COMPLEX_ONE));\n    }\n\n    @Test\n    public void testInsertWithSelect() {\n        var sql = \"INSERT INTO Customers (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers;\";\n        assertEquals(\"INSERT INTO Customers_r (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDelete2() {\n        var sql = \"DELETE FROM validation_task WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf WHERE conf_name IN ('SaleValidation'))\";\n        assertEquals(\"DELETE FROM validation_task_r WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf_r WHERE conf_name IN ('SaleValidation'))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOracleSpecialDelete() {\n        var sql = \"delete table1 where column_name=xyz\";\n        assertEquals(\"DELETE table1_r WHERE column_name = xyz\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter() {\n        var sql = \"ALTER TABLE Persons ADD UNIQUE (P_Id)\";\n        assertEquals(\"ALTER TABLE Persons_r ADD UNIQUE (P_Id)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter2() {\n        var sql = \"ALTER TABLE table_name MODIFY coluname datatype\";\n        assertEquals(\"ALTER TABLE table_name_r MODIFY coluname datatype\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDrop() {\n        var sql = \"DROP table tname;\\n\\r\";\n        assertEquals(\"DROP table tname_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropFunction() {\n        var sql = \"DROP FUNCTION functionName;\";\n        assertEquals(\"DROP FUNCTION functionName\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropProcedure() {\n        var sql = \"drop procedure procedureName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropView() {\n        var sql = \"DROP view viewName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropIndex() {\n        var sql = \"DROP INDEX indexName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUnionAll() {\n        var sql = \"SELECT coluname(s) FROM table1 UNION ALL SELECT coluname(s) FROM table2;\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r UNION ALL SELECT coluname(s) FROM table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMerge() {\n        var sql = \"MERGE INTO employees e  USING hr_records h  ON (e.id = h.emp_id) WHEN MATCHED THEN  UPDATE SET e.address = h.address  WHEN NOT MATCHED THEN    INSERT (id, address) VALUES (h.emp_id, h.address);\";\n        assertEquals(\"MERGE INTO employees_r e USING hr_records_r h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeUsingQuery() {\n        var sql = \"MERGE INTO employees e USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h  ON (e.id = h.emp_id)  WHEN MATCHED THEN  UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\";\n        assertEquals(\"MERGE INTO employees_r e USING (SELECT * FROM hr_records_r WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeComplexQuery() {\n        assertEquals(\"MERGE INTO  cf_procedure_r proc USING ( WITH NON_STRATEGY_DETAILS AS   (   SELECT /*+ materialize*/ cf_strategy_id    FROM     ( SELECT  strat.cf_strategy_id        FROM cf_strategy_r strat,             struct_doc_Sect_ver_r prodGrp        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id                 AND strat.src_mgr_id     = prodGrp.mgr_id                 AND strat.src_ver_num    = prodGrp.ver_num                 AND strat.module_type   IN ('COMPL','PRCMSTR')   )  ),   NON_STRATEGY_COMPS AS   (   SELECT /*+ materialize*/ cf_component_id   FROM   (     SELECT comp.cf_component_id AS cf_component_id     FROM   cf_component_r comp,            tier_basis_ver_r tb     WHERE  comp.bucket_src_id   = tb.tier_basis_id             AND comp.bucket_src_mgr_id  = tb.mgr_id             AND comp.bucket_src_ver_num = tb.ver_num             AND comp.module_type       IN ('COMPL','PRCMSTR')   )   ) , NON_STRAT_PERIODS AS (   SELECT /*+ materialize*/ cf_period_id   FROM         cf_period_r per,         struct_doc_sect_ver_r prodGrp   WHERE  per.src_id            = prodGrp.struct_doc_sect_id         AND per.src_mgr_id     = prodGrp.mgr_id         AND per.src_ver_num    = prodGrp.ver_num         AND per.module_type    IN ('COMPL','PRCMSTR')         AND per.pmt_status NOT IN ('TERM','REV')    SELECT DISTINCT cf_procedure_id   FROM     (SELECT /*+ LEADING(comp,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  non_strategy_comps_r comp,           cf_procedure_r proc     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'           AND comp.cf_component_id    = proc.cf_component_id    UNION ALL     SELECT  /*+ LEADING(strat,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  cf_procedure_r proc,           non_strategy_details_r strat     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND strat.cf_strategy_id = proc.cf_strategy_id     UNION ALL     SELECT  /*+ LEADING(strat,proc)*/          proc.cf_procedure_id AS cf_procedure_id     FROM cf_procedure_r proc,          non_strat_periods_r periods     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND periods.CF_PERIOD_ID = proc.period_id     )      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id) WHEN MATCHED THEN   UPDATE SET proc.variable_name = 'TierResultSSName';\", interceptor.changeTable(SQL_MERGE_COMPLEX));\n    }\n\n    @Test\n    public void testMergeComplexQuery2() {\n        assertEquals(\"MERGE INTO cf_procedure_ver_r procVer USING (SELECT cf_procedure_id FROM cf_procedure_r proc WHERE proc.variable_name = 'TierResultSSName') proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id) WHEN MATCHED THEN UPDATE SET procVer.variable_name = 'TierResultSSName' WHERE procVer.variable_name <> 'TierResultSSName'\", interceptor.changeTable(SQL_MERGE_COMPLEX_TWO));\n    }\n\n    @Test\n    public void testCreateTable2() {\n        var sql = \"CREATE TABLE Persons(PersonID int,LastName varchar(255),FirstName varchar(255),Address varchar(255),City varchar(255));\";\n        assertEquals(\"CREATE TABLE Persons_r (PersonID int, LastName varchar (255), FirstName varchar (255), Address varchar (255), City varchar (255))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateGlobalTable() {\n        var sql = \"CREATE GLOBAL TEMPORARY TABLE excl_cust (gen_name VARCHAR2(100),run_date TIMESTAMP(3), item_root_uuid  VARCHAR2(22), owner_member_id  NUMBER(20)) ON COMMIT DELETE ROWS\";\n        assertEquals(\"CREATE GLOBAL TEMPORARY TABLE excl_cust_r (gen_name VARCHAR2 (100), run_date TIMESTAMP (3), item_root_uuid VARCHAR2 (22), owner_member_id NUMBER (20)) ON COMMIT DELETE ROWS\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateIndex() {\n        var sql = \"CREATE INDEX temp_name_idx ON table1(name) NOLOGGING PARALLEL (DEGREE 8);\";\n        assertEquals(\"CREATE INDEX temp_name_idx ON table1_r (name) NOLOGGING PARALLEL (DEGREE8)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView() {\n        var sql = \"CREATE VIEW dept AS SELECT * FROM dept;\";\n        assertEquals(\"CREATE VIEW dept AS SELECT * FROM dept_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView2() {\n        var sql = \"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp, dept WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno;\";\n        assertEquals(\"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp_r, dept_r WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateType() {\n        var sql = \"CREATE OR REPLACE TYPE TYPE_NAME IS TABLE OF VARCHAR2(100)\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTable() {\n        var sql = \"UPDATE tableName SET column1 = expression1, column2 = expression2\";\n        assertEquals(\"UPDATE tableName_r SET column1 = expression1, column2 = expression2\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery() {\n        var sql = \"UPDATE table1 SET table1.value = (SELECT table2.CODE FROM table2 WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE='blah' AND EXISTS (SELECT table2.CODE  FROM table2    WHERE table1.value = table2.DESC);\";\n        assertEquals(\"UPDATE table1_r SET table1.value = (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE = 'blah' AND EXISTS (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery2() {\n        var sql = \"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1 INNER JOIN table2 ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\";\n        assertEquals(\"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1_r INNER JOIN table2_r ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQueryWithOracleHint() {\n        var sql = \"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\";\n        assertEquals(\"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible_r ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testTruncateTable() {\n        var sql = \"truncate table eligible_item\";\n        assertEquals(\"TRUNCATE TABLE eligible_item_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithComment() {\n        var sql = \"select * from foo -- this is a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentContainingKeyword() {\n        var sql = \"select * from foo -- what happens if I say update in a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentEndingWithKeyword() {\n        var sql = \"select * from foo -- what happens if I end a comment with an update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsAndNewlines() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n\\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectForUpdate() {\n        //TODO 暂时解决不能使用的问题,当碰到for update nowait这样的,后面的 nowait 会被当做成表但也不是很影响苗老板的动态表过滤.\n        var sql = \"select * from mp where id = 1 for update\";\n        assertEquals(\"SELECT * FROM mp_r WHERE id = 1 FOR UPDATE\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOnDuplicateKeyUpdate () {\n        var sql = \"INSERT INTO cf_procedure (_id,password) VALUES ('1','password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword';\";\n        assertEquals(\"INSERT INTO cf_procedure_r (_id, password) VALUES ('1', 'password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateIgnore() {\n        var sql = \"update ignore student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n\n        sql = \"UPDATE IGNORE student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertIgnore() {\n        var sql = \"INSERT IGNORE INTO student (userid,username) VALUES (2,'swan'),(4,'bear') ;\";\n        assertEquals(\"INSERT IGNORE INTO student_r (userid, username) VALUES (2, 'swan'), (4, 'bear')\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateUniqueIndex() {\n        var sql = \"CREATE UNIQUE INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE UNIQUE INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1_r ADD UNIQUE INDEX `a` (`a`)\";\n        assertEquals(\"ALTER TABLE table1_r_r ADD UNIQUE INDEX `a` (`a`)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateFullTextIndex(){\n        var sql = \"CREATE FULLTEXT INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE FULLTEXT INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1 ADD FULLTEXT INDEX `a`(`a`)\";\n        assertEquals(\"ALTER TABLE table1_r ADD FULLTEXT INDEX `a`(`a`)\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void test() {\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t LEFT JOIN entity_r e ON e.id = t.id\", interceptor.changeTable(origin));\n    }\n\n    @Test\n    void testCreateTable() {\n        var sql = \"\"\"\n            CREATE TABLE `tag`  (\n              `id` int(11) NOT NULL AUTO_INCREMENT,\n              `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字',\n              `type` int(11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别',\n              PRIMARY KEY (`id`) USING BTREE\n            ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic;\n            \"\"\";\n        assertEquals(\"CREATE TABLE `tag_r` (`id` int (11) NOT NULL AUTO_INCREMENT, `name` varchar (50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字', `type` int (11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别', PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateTableIfNotExists() {\n        var sql = \"\"\"\n            CREATE TABLE IF NOT EXISTS `user_info` (\n                `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n                `username` VARCHAR(50) NOT NULL UNIQUE,\n                `email` VARCHAR(100) NOT NULL UNIQUE,\n                `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n            \"\"\";\n        assertEquals(\"CREATE TABLE IF NOT EXISTS `user_info_r` (`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR (50) NOT NULL UNIQUE, `email` VARCHAR (100) NOT NULL UNIQUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDropTableIfExists() {\n        var sql = \"DROP TABLE IF EXISTS `tag`\";\n        assertEquals(\"DROP TABLE IF EXISTS `tag_r`\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testIssues6730() {\n        // https://github.com/baomidou/mybatis-plus/issues/6730\n        var sql = \"select * from user order by top_bottom_sort desc, 0- EXTRACT(EPOCH FROM req_delivery_time) desc\";\n        assertEquals(\"SELECT * FROM user_r ORDER BY top_bottom_sort DESC, 0 - EXTRACT(EPOCH FROM req_delivery_time) DESC\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectJoin() {\n        var sql = \"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\";\n        assertEquals(\"SELECT * FROM entity_r e JOIN entity1_r e1 ON e1.id = e.id WHERE e.id = ? OR e.name = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectWithAs() {\n        var sql = \"with with_as_A as (select * from entity) select * from with_as_A\";\n        assertEquals(\"WITH with_as_A AS (SELECT * FROM entity_r) SELECT * FROM with_as_A_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDuplicateKeyUpdate() {\n        var sql = \"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\";\n        assertEquals(\"INSERT INTO entity_r (name, age) VALUES ('秋秋', 18), ('秋秋', '22') ON DUPLICATE KEY UPDATE age = 18\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDelete() {\n        var sql = \"delete from entity where id = ?\";\n        assertEquals(\"DELETE FROM entity_r WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testUpdate() {\n        var sql = \"update entity set name = ? where id = ?\";\n        assertEquals(\"UPDATE entity_r SET name = ? WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testPartition() {\n        // 这种jsql解析不了\n        var sql = \"\"\"\n            -- 查询2023年Q2分区数据\n            SELECT\\s\n                region,\n                SUM(gross_profit) AS 区域总利润,\n                AVG(order_value) AS 平均订单金额\n            FROM\\s\n                sales_data\n            PARTITION BY\\s\n                (TO_DATE(order_date, 'YYYY-MM-DD'))\n            INTERVAL MONTHLY\n            FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\n            GROUP BY\\s\n                region;\n            \"\"\";\n        assertEquals(\"-- 查询2023年Q2分区数据\\n\" +\n            \"SELECT \\n\" +\n            \"    region,\\n\" +\n            \"    SUM(gross_profit) AS 区域总利润,\\n\" +\n            \"    AVG(order_value) AS 平均订单金额\\n\" +\n            \"FROM \\n\" +\n            \"    sales_data_r\\n\" +\n            \"PARTITION BY \\n\" +\n            \"    (TO_DATE(order_date, 'YYYY-MM-DD'))\\n\" +\n            \"INTERVAL MONTHLY\\n\" +\n            \"FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\\n\" +\n            \"GROUP BY \\n\" +\n            \"    region;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test2() {\n        // TODO 这里Jsql解析失败了....\n        var sql = \"\"\"\n            SELECT\\s\n                COUNT(*) AS 订单总数,\n                SUM(o.order_total) AS 总销售额,\n                SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数\n            FROM\\s\n                orders o\n            JOIN\\s\n                customers c ON o.customer_id = c.customer_id\n            JOIN\\s\n                order_items oi ON o.order_id = oi.order_id\n            WHERE\\s\n                c.region = 'North America'\n                AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30'\n            GROUP BY\\s\n                o.customer_id\n            HAVING\\s\n                COUNT(*) > 10;\n            ORDER BY\\s\n                total_sales DESC;\n            \"\"\";\n        assertEquals(\"SELECT \\n\" +\n            \"    COUNT(*) AS 订单总数,\\n\" +\n            \"    SUM(o.order_total) AS 总销售额,\\n\" +\n            \"    SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数\\n\" +\n            \"FROM \\n\" +\n            \"    orders_r o\\n\" +\n            \"JOIN \\n\" +\n            \"    customers_r c ON o.customer_id = c.customer_id\\n\" +\n            \"JOIN \\n\" +\n            \"    order_items_r oi ON o.order_id = oi.order_id\\n\" +\n            \"WHERE \\n\" +\n            \"    c.region = 'North America'\\n\" +\n            \"    AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30'\\n\" +\n            \"GROUP BY \\n\" +\n            \"    o.customer_id\\n\" +\n            \"HAVING \\n\" +\n            \"    COUNT(*) > 10;\\n\" +\n            \"ORDER BY \\n\" +\n            \"    total_sales DESC;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test3() {\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            DELIMITER $$\n            DECLARE\\s\n                cur CURSOR FOR\\s\n                    SELECT employee_id FROM employees WHERE salary < 50000;\n                emp_id INT;\n            BEGIN\n                OPEN cur;\n                WHILE TRUE DO\n                    FETCH cur INTO emp_id;\n                    IF cur_rowcount = 0 THEN\n                        LEAVE;\n                    END IF;\n                   \\s\n                    UPDATE employees\\s\n                    SET salary = salary * 1.1\\s\n                    WHERE employee_id = emp_id;\n                   \\s\n                    INSERT INTO audit_log (employee_id, old_salary, new_salary)\n                    VALUES (emp_id, salary_before_update, salary_after_update);\n                END WHILE;\n                CLOSE cur;\n            END\n            $$\n            DELIMITER ;\n            \"\"\";\n        assertEquals(\"DELIMITER $$\\n\" +\n            \"DECLARE \\n\" +\n            \"    cur CURSOR FOR \\n\" +\n            \"        SELECT employee_id FROM employees_r WHERE salary < 50000;\\n\" +\n            \"    emp_id INT;\\n\" +\n            \"BEGIN\\n\" +\n            \"    OPEN cur;\\n\" +\n            \"    WHILE TRUE DO\\n\" +\n            \"        FETCH cur INTO emp_id_r;\\n\" +\n            \"        IF cur_rowcount = 0 THEN\\n\" +\n            \"            LEAVE;\\n\" +\n            \"        END IF;\\n\" +\n            \"        \\n\" +\n            \"        UPDATE employees_r \\n\" +\n            \"        SET salary = salary * 1.1 \\n\" +\n            \"        WHERE employee_id = emp_id;\\n\" +\n            \"        \\n\" +\n            \"        INSERT INTO audit_log_r (employee_id, old_salary, new_salary)\\n\" +\n            \"        VALUES (emp_id, salary_before_update, salary_after_update);\\n\" +\n            \"    END WHILE;\\n\" +\n            \"    CLOSE cur;\\n\" +\n            \"END\\n\" +\n            \"$$\\n\" +\n            \"DELIMITER ;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test4() {\n        var sql = \"\"\"\n            SELECT *\n            FROM employees e\n            JOIN departments d ON e.department_id = d.department_id\n            WHERE\\s\n                e.last_name LIKE CONCAT('%', :lastName, '%')\n                AND (\n                    d.department_name IN (:departmentList)\n                    OR :departmentList IS NULL\n                )\n                AND (\n                    e.hire_date >= :startDate\n                    OR :startDate IS NULL\n                )\n            ORDER BY\\s\n                e.employee_id\n            \"\"\";\n        assertEquals(\"SELECT * FROM employees_r e JOIN departments_r d ON e.department_id = d.department_id WHERE e.last_name LIKE CONCAT('%', :lastName, '%') AND (d.department_name IN (:departmentList) OR :departmentList IS NULL) AND (e.hire_date >= :startDate OR :startDate IS NULL) ORDER BY e.employee_id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test5() {\n        var sql  = \"\"\"\n            SELECT\\s\n                product_id,\n                product_name,\n                stock_quantity,\n                (SELECT\\s\n                    SUM(ordered_qty)\\s\n                 FROM\\s\n                    purchase_orders po\\s\n                 WHERE\\s\n                    po.product_id = products.product_id\\s\n                    AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume\n            FROM\\s\n                products\n            WHERE\\s\n                stock_quantity < (\n                    SELECT\\s\n                        AVG(recommended_stock)\\s\n                    FROM\\s\n                        product_settings\\s\n                    WHERE\\s\n                        product_id = products.product_id\n                )\n                AND recent_order_volume > 500\n            \"\"\";\n        assertEquals(\"SELECT product_id, product_name, stock_quantity, (SELECT SUM(ordered_qty) FROM purchase_orders_r po WHERE po.product_id = products.product_id AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume FROM products_r WHERE stock_quantity < (SELECT AVG(recommended_stock) FROM product_settings_r WHERE product_id = products.product_id) AND recent_order_volume > 500\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test6() {\n        var sql = \"\"\"\n            WITH user_activity AS (\n                SELECT\\s\n                    user_id,\n                    event_type,\n                    event_time,\n                    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq\n                FROM\\s\n                    user_events\n            )\n            SELECT\\s\n                user_id,\n                event_type,\n                event_time,\n                LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time\n            FROM\\s\n                user_activity\n            WHERE\\s\n                activity_seq = 5\n            \"\"\";\n        assertEquals(\"WITH user_activity AS (SELECT user_id, event_type, event_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq FROM user_events_r) SELECT user_id, event_type, event_time, LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time FROM user_activity_r WHERE activity_seq = 5\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test7() {\n        var sql = \"select * from db1.test where a = ?\";\n        assertEquals(\"SELECT * FROM db1.test_r WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test8() {\n        // 这种jsql解析不了的\n        var sql = \"SELECT * FROM [HR].[dbo].[Employee_Salary_2023];\";\n        assertEquals(\"SELECT * FROM [HR].[dbo].[Employee_Salary_2023_r];\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test9(){\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            SELECT * FROM [SalesDB].[dbo].[Orders]\n            JOIN [MarketingDB].[dbo].[Customers]\\s\n            ON Orders.CustomerID = Customers.CustomerID;\n            \"\"\";\n        assertEquals(\"SELECT * FROM [SalesDB].[dbo].[Orders_r]\\n\" +\n            \"JOIN [MarketingDB].[dbo].[Customers_r] \\n\" +\n            \"ON Orders.CustomerID = Customers.CustomerID;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test10() {\n        var sql = \"\"\"\n            SELECT * FROM ecommerce_orders\\s\n            PARTITION (p2022, p2023)\n            WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31';\n            \"\"\";\n        assertEquals(\"SELECT * FROM ecommerce_orders_r PARTITION(p2022, p2023) WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test11() {\n        var sql = \"\"\"\n            SELECT order_id, customer_id,amount,\n                  RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank\n                FROM orders;\n            \"\"\";\n        assertEquals(\"SELECT order_id, customer_id, amount, RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank FROM orders_r\", interceptor.changeTable(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/IllegalSQLInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.h2.jdbcx.JdbcDataSource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * @author miemie\n * @since 2022-04-11\n */\nclass IllegalSQLInnerInterceptorTest {\n\n    private final IllegalSQLInnerInterceptor interceptor = new IllegalSQLInnerInterceptor();\n\n    private static DataSource dataSource;\n\n    @BeforeAll\n    public static void beforeAll() throws SQLException {\n        var jdbcDataSource = new JdbcDataSource();\n        jdbcDataSource.setURL(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        jdbcDataSource.setPassword(\"\");\n        jdbcDataSource.setUser(\"sa\");\n        dataSource = jdbcDataSource;\n        Connection connection = jdbcDataSource.getConnection();\n        var sql = \"\"\"\n            CREATE TABLE T_DEMO (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index1` (`a`,`b`)\n            );\n            CREATE TABLE T_TEST (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index2` (`a`,`b`)\n            );\n            \"\"\";\n        SqlRunner sqlRunner = new SqlRunner(connection);\n        sqlRunner.run(sql);\n    }\n\n    @Test\n    void test() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT COUNT(*) AS total FROM t_user WHERE (client_id = ?)\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete from t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age != 1\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where (age = 1 or name = 'test')\", null));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where  b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.`T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `test` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN PUBLIC.`T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN PUBLIC.`T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (c = 3 OR b = 2)\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3 OR b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 3 AND (c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) or b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) AND b = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 AND b = 2)\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` a INNER JOIN T_TEST b ON a.a = b.a where a.a = 3 AND (b.c = 3 OR b.b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a != (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        //TODO 低版本这里的抛异常了.看着应该不用抛出\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n    }\n    @Test\n    void testCount() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a) c\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO`) a \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1)) a \", dataSource.getConnection()));\n    }\n\n    @Test\n    void testCatalogAndSchemaName() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from TEST.PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        // 非同一模式,读不到索引的情况\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from PUBLIC.DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/MultiDataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport com.google.common.collect.HashBasedTable;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.schema.Table;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL多表场景的数据权限拦截器测试\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic class MultiDataPermissionInterceptorTest {\n    private static final Logger logger = LoggerFactory.getLogger(MultiDataPermissionInterceptorTest.class);\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final com.google.common.collect.Table<String, String, String> sqlSegmentMap;\n    private static final DataPermissionInterceptor interceptor;\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n    private static final String TEST_6 = \"com.baomidou.roleMapper.selectUserInfo\";\n    private static final String TEST_7 = \"com.baomidou.roleMapper.summarySum\";\n    private static final String TEST_8_1 = \"com.baomidou.CustomMapper.selectByOnlyMyData\";\n    private static final String TEST_8_2 = \"com.baomidou.CustomMapper.selectByOnlyOrgData\";\n    private static final String TEST_8_3 = \"com.baomidou.CustomMapper.selectByOnlyDeptData\";\n    private static final String TEST_8_4 = \"com.baomidou.CustomMapper.selectByMyDataOrDeptData\";\n    private static final String TEST_8_5 = \"com.baomidou.CustomMapper.selectByMyData\";\n\n    static {\n        sqlSegmentMap = HashBasedTable.create();\n        sqlSegmentMap.put(TEST_1, \"sys_user\", \"username='123' or userId IN (1,2,3)\");\n        sqlSegmentMap.put(TEST_2, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_3, \"sys_role\", \"companyId in (1,2,3)\");\n        sqlSegmentMap.put(TEST_4, \"sys_role\", \"username like 'abc%'\");\n        sqlSegmentMap.put(TEST_5, \"sys_role\", \"id=1 and role_id in (select id from sys_role)\");\n        sqlSegmentMap.put(TEST_6, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_6, \"sys_user_role\", \"r.role_id=3 AND r.role_id IN (7,9,11)\");\n        sqlSegmentMap.put(TEST_7, \"`fund`\", \"a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111\");\n        sqlSegmentMap.put(TEST_7, \"`fund_month`\", \"b.fund_id = 2 AND b.month <= '2022-05'\");\n        sqlSegmentMap.put(TEST_8_1, \"fund\", \"user_id=1\");\n        sqlSegmentMap.put(TEST_8_2, \"fund\", \"org_id=1\");\n        sqlSegmentMap.put(TEST_8_3, \"fund\", \"dept_id=1\");\n        sqlSegmentMap.put(TEST_8_4, \"fund\", \"user_id=1 or dept_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table1\", \"u.user_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table2\", \"u.dept_id=1\");\n        interceptor = new DataPermissionInterceptor(new MultiDataPermissionHandler() {\n\n            @Override\n            public Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId) {\n                try {\n                    String sqlSegment = sqlSegmentMap.get(mappedStatementId, table.getName());\n                    if (sqlSegment == null) {\n                        logger.info(\"{} {} AS {} : NOT FOUND\", mappedStatementId, table.getName(), table.getAlias());\n                        return null;\n                    }\n                    if (table.getAlias() != null) {\n                        // 替换表别名\n                        sqlSegment = sqlSegment.replaceAll(\"u\\\\.\", table.getAlias().getName() + StringPool.DOT);\n                    }\n                    Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n                    logger.info(\"{} {} AS {} : {}\", mappedStatementId, table.getName(), table.getAlias(), sqlSegmentExpression.toString());\n                    return sqlSegmentExpression;\n                } catch (JSQLParserException e) {\n                    logger.error(\"解析错误:\", e);\n                }\n                return null;\n            }\n        });\n    }\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    @Test\n    void test6() {\n        // 显式指定 JOIN 类型时 JOIN 右侧表才能进行拼接条件\n        assertSql(TEST_6, \"select u.username from sys_user u LEFT join sys_user_role r on u.id=r.user_id\",\n            \"SELECT u.username FROM sys_user u LEFT JOIN sys_user_role r ON u.id = r.user_id AND r.role_id = 3 AND r.role_id IN (7, 9, 11) WHERE u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test7() {\n        assertSql(TEST_7, \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = TRUE) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\",\n            \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = TRUE AND b.fund_id = 2 AND b.month <= '2022-05' WHERE a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\");\n    }\n\n    @Test\n    void test8() {\n        assertSql(TEST_8_1, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1\");\n        assertSql(TEST_8_2, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND org_id = 1\");\n        assertSql(TEST_8_3, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND dept_id = 1\");\n        assertSql(TEST_8_4, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1 OR dept_id = 1\");\n        // 修改之前旧版的多表数据权限对这个SQL的表现形式：\n        // 输入 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\"\n        // 输出 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid) SELECT * FROM temp\"\n        // 修改之后的多表数据权限对这个SQL的表现形式\n        assertSql(TEST_8_5, \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\",\n            \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid AND t2.dept_id = 1 WHERE t1.user_id = 1) SELECT * FROM temp\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(interceptor.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/PaginationInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass PaginationInnerInterceptorTest {\n\n    private final PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();\n\n    @Test\n    void optimizeCount() {\n        /* 能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select distinct id from table order by id\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n\n        assertsCountSql(\"select distinct id from table\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n    }\n\n    @Test\n    void notOptimizeCount() {\n        /* 不能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? where u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? WHERE u.xx = ?\");\n\n        /* join 表与 where 条件大小写不同的情况 */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id where R.NAME = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE R.NAME = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\");\n    }\n\n    @Test\n    void optimizeCountOrderBy() {\n        /* order by 里不带参数,去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY name\",\n            \"SELECT COUNT(*) AS total FROM comment\");\n\n        /* order by 里带参数,不去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\",\n            \"SELECT COUNT(*) AS total FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\");\n    }\n\n    @Test\n    void withAsCount() {\n        assertsCountSql(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT COUNT(*) AS total FROM A\");\n    }\n\n    @Test\n    void withAsOrderBy() {\n        assertsConcatOrderBy(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT * FROM A ORDER BY column ASC\",\n            OrderItem.asc(\"column\"));\n    }\n\n    @Test\n    void groupByCount() {\n        assertsCountSql(\"SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)\",\n            \"SELECT COUNT(*) FROM (SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)) TOTAL\");\n    }\n\n    @Test\n    void leftJoinSelectCount() {\n        assertsCountSql(\"select r.id, r.name, r.phone,rlr.total_top_up from reseller r \" +\n                \"left join (select ral.reseller_id, sum(ral.top_up_money) as total_top_up, sum(ral.acquire_money) as total_acquire \" +\n                \"from reseller_acquire_log ral \" +\n                \"group by ral.reseller_id) rlr on r.id = rlr.reseller_id \" +\n                \"order by r.created_at desc\",\n            \"SELECT COUNT(*) AS total FROM reseller r\");\n\n        // 不优化\n        assertsCountSql(\"SELECT f.ca, f.cb FROM table_a f LEFT JOIN \" +\n                \"(SELECT ca FROM table_b WHERE cc = ?) rf on rf.ca = f.ca\",\n            \"SELECT COUNT(*) AS total FROM table_a f LEFT JOIN (SELECT ca FROM table_b WHERE cc = ?) rf ON rf.ca = f.ca\");\n\n        assertsCountSql(\"select * from order_info left join (select count(1) from order_info where create_time between ? and ?) tt on 1=1 WHERE equipment_id=?\",\n            \"SELECT COUNT(*) AS total FROM order_info LEFT JOIN (SELECT count(1) FROM order_info WHERE create_time BETWEEN ? AND ?) tt ON 1 = 1 WHERE equipment_id = ?\");\n    }\n\n    void assertsCountSql(String sql, String targetSql) {\n        assertThat(interceptor.autoCountSql(new Page<>(), sql)).isEqualTo(targetSql);\n    }\n\n    void assertsConcatOrderBy(String sql, String targetSql, OrderItem... orderItems) {\n        assertThat(interceptor.concatOrderBy(sql, Arrays.asList(orderItems))).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/TenantLineInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-07-30\n */\nclass TenantLineInnerInterceptorTest {\n\n    private static final Map<String, String> FIRS_RESULT_TMAP = new HashMap<>();\n\n    private static final Map<String, String> LAST_RESULT_TMAP = new HashMap<>();\n\n    static {\n        firstResultMap();\n        lastResultMap();\n    }\n\n    static void firstResultMap() {\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        FIRS_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE tenant_id = 1 AND id = ?\");\n\n        // set subSelect\n        FIRS_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?) WHERE e.tenant_id = 1 AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        /* not */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND NOT (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE u.tenant_id = 1 AND NOT (u.id = ? OR u.name = ?)\");\n\n        /* in */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在最前\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n        // 在最后\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在中间\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* inner not = */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.tenant_id = 1 AND e.id = ? LIMIT 1)\");\n\n        /* NOT EXISTS */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* >= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <> */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t2.tenant_id = 1 AND t1.col1 = t2.col1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.tenant_id = 1 AND e2.id = 1) = 1, e2.type, e1.type) AS type FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?\");\n\n        // left join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e.tenant_id = 1\");\n\n        // right join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e RIGHT JOIN entity1 e1 ON e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id INNER JOIN entity2 e2 ON e.tenant_id = 1 AND e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) WHERE e1.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) RIGHT JOIN entity3 e3 ON e1.tenant_id = 1 AND e1.id = e3.id WHERE e3.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) ON e2.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e1.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e.tenant_id = 1 AND e.id = e2.id WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN with_as_A e2 ON e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        // inner join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE e.id = ? OR e.name = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN with_as_1 w1 ON e.tenant_id = 1 AND w1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b WHERE a.tenant_id = 1 AND a.id = b.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        // 沙雕的括号写法\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        FIRS_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n    static void lastResultMap() {\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        LAST_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE id = ? AND tenant_id = 1\");\n\n        // set subSelect\n        LAST_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1) \" +\n                \"WHERE e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        /* not */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE NOT (u.id = ? OR u.name = ?) AND u.tenant_id = 1\");\n\n        /* in */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在最前\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n        // 在最后\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在中间\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* inner not = */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1)) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? AND e.tenant_id = 1 LIMIT 1)\");\n\n        /* NOT EXISTS */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* >= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* <= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* <> */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1)\");\n\n        LAST_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t1.col1 = t2.col1 AND t2.tenant_id = 1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1 AND e2.tenant_id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1\");\n\n        // left join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        // right join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"WHERE e1.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 \" +\n                \"WHERE e3.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id AND e2.tenant_id = 1 \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        // inner join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE e.id = ? OR e.name = ?\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN with_as_1 w1 ON w1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 \" +\n                \"WHERE e.id = e1.id AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id AND a.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        // 沙雕的括号写法\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        LAST_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n\n    private final TenantLineInnerInterceptor interceptor = new TenantLineInnerInterceptor(new TenantLineHandler() {\n        private boolean ignoreFirst;// 需要执行 getTenantId 前必须先执行 ignoreTable\n\n        @Override\n        public Expression getTenantId() {\n            assertThat(ignoreFirst).isEqualTo(true);\n            ignoreFirst = false;\n            return new LongValue(1);\n        }\n\n        @Override\n        public boolean ignoreTable(String tableName) {\n            ignoreFirst = true;\n            return tableName.startsWith(\"with_as\");\n        }\n    });\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    public void test(ExpressionAppendMode appendMode) {\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == appendMode ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertMap.forEach((k, v) -> assertSql(k, appendMode));\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void insert(ExpressionAppendMode appendMode) {\n        // plain\n        assertSql(\"insert into entity (id) values (?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?)\", appendMode);\n        // batch\n        assertSql(\"insert into entity (id) values (?),(?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?),(?,?)\", appendMode);\n        // 无 insert的列\n        assertSql(\"insert into entity value (?,?)\", appendMode);\n        // 自己加了insert的列\n        assertSql(\"insert into entity (id,name,tenant_id) value (?,?,?)\", appendMode);\n        // insert into select\n        assertSql(\"insert into entity (id,name) select id,name from entity2\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from entity2 e2\", appendMode);\n        assertSql(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void delete(ExpressionAppendMode appendMode) {\n        assertSql(\"delete from entity where id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void update(ExpressionAppendMode appendMode) {\n        assertSql(\"update entity set name = ? where id = ?\", appendMode);\n        // set subSelect\n        assertSql(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\", appendMode);\n\n        assertSql(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\", appendMode);\n\n        assertSql(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingle(ExpressionAppendMode appendMode) {\n        // 单表\n        assertSql(\"select * from entity where id = ?\", appendMode);\n\n        assertSql(\"select * from entity where id = ? or name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", appendMode);\n\n        /* not */\n        assertSql(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectIn(ExpressionAppendMode appendMode) {\n        /* in */\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在最前\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n        // 在最后\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在中间\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectEq(ExpressionAppendMode appendMode) {\n        /* = */\n        assertSql(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectInnerNotEq(ExpressionAppendMode appendMode) {\n        /* inner not = */\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectExists(ExpressionAppendMode appendMode) {\n        /* EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n\n        assertSql(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", appendMode);\n\n        /* NOT EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWhereSubSelect(ExpressionAppendMode appendMode) {\n        /* >= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <> */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectFromSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodySubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodyFuncSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n            \"FROM entity e1 WHERE e1.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoin(ExpressionAppendMode appendMode) {\n        // left join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectRightJoin(ExpressionAppendMode appendMode) {\n        // right join\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectMixJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n    }\n\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectJoinSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select * from (select * from entity e) e1 \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n\n        assertSql(\"select * from entity1 e1 \" +\n            \"left join (select * from entity2 e2) e22 \" +\n            \"on e1.id = e22.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n            \"right join entity3 e3 on e1.id = e3.id\", appendMode);\n\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoinMultipleTrailingOn(ExpressionAppendMode appendMode) {\n        // 多个 on 尾缀的\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectInnerJoin(ExpressionAppendMode appendMode) {\n        // inner join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // ignore table\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join with_as_1 w1 on w1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity e,entity1 e1 \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        assertSql(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        // SubJoin with 隐式内连接\n        assertSql(\"SELECT * FROM (entity e,entity1 e1) \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        assertSql(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        assertSql(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        // 沙雕的括号写法\n        assertSql(\"SELECT * FROM (((entity e,entity1 e1))) WHERE e.id = e1.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingleJoin(ExpressionAppendMode appendMode) {\n        // join\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWithAs(ExpressionAppendMode appendMode) {\n        assertSql(\"with with_as_A as (select * from entity) select * from with_as_A\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void testDuplicateKeyUpdate(ExpressionAppendMode appendMode) {\n        assertSql(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\", appendMode);\n    }\n\n    void assertSql(String sql, ExpressionAppendMode appendMode) {\n        interceptor.setExpressionAppendMode(appendMode);\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == interceptor.getExpressionAppendMode() ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertThat(interceptor.parserSingle(sql, null)).isEqualTo(assertMap.get(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-4.9/src/test/java/com/baomidou/mybatisplus/test/plugins/pagination/SelectBodyToPlainSelectTest.java",
    "content": "package com.baomidou.mybatisplus.test.plugins.pagination;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SelectBody强转PlainSelect不支持sql里面最外层带union\n * 用SetOperationList处理sql带union的语句\n */\nclass SelectBodyToPlainSelectTest {\n\n    private static final List<OrderItem> ITEMS = new ArrayList<>();\n\n    static {\n        ITEMS.add(OrderItem.asc(\"column\"));\n    }\n\n    /**\n     * 报错的测试\n     */\n    @Test\n    void testSelectBodyToPlainSelectThrowException() {\n        Select selectStatement = null;\n        try {\n            String originalUnionSql = \"select * from test union select * from test\";\n            selectStatement = (Select) CCJSqlParserUtil.parse(originalUnionSql);\n        } catch (JSQLParserException e) {\n            e.printStackTrace();\n        }\n        assert selectStatement != null;\n        Select finalSelectStatement = selectStatement;\n        Assertions.assertThrows(ClassCastException.class, () -> {\n            PlainSelect plainSelect = (PlainSelect) finalSelectStatement.getSelectBody();\n        });\n    }\n\n    @BeforeEach\n    void setup() {\n        List<OrderItem> orderItems = new ArrayList<>();\n        OrderItem order = new OrderItem();\n        order.setAsc(true);\n        order.setColumn(\"column\");\n        orderItems.add(order);\n        OrderItem orderEmptyColumn = new OrderItem();\n        orderEmptyColumn.setAsc(false);\n        orderEmptyColumn.setColumn(\"\");\n        orderItems.add(orderEmptyColumn);\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByBefore() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFix() {\n        List<OrderItem> orderList = new ArrayList<>();\n        // 测试可能的 sql 注入 https://github.com/baomidou/mybatis-plus/issues/5745\n        orderList.add(OrderItem.asc(\"col umn\"));\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union select * from test2\", orderList);\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test UNION SELECT * FROM test2 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union all select * from test2\", orderList);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test UNION ALL SELECT * FROM test2 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFixWithWhere() {\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union select * from test2 where 1 = 1\", ITEMS);\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union all select * from test2 where 1 = 1 \", ITEMS);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION ALL SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorOrderByEmptyColumnFix() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/build.gradle",
    "content": "compileJava {\n    options.release = 11\n}\n\ndependencies {\n    api \"com.github.jsqlparser:jsqlparser:5.0\"\n    api project(\":mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-common\")\n    implementation \"${lib.\"slf4j-api\"}\"\n    implementation \"de.ruedigermoeller:fst:3.0.3\"\n    implementation \"com.github.ben-manes.caffeine:caffeine:2.9.3\"\n    testImplementation \"io.github.classgraph:classgraph:4.8.177\"\n    testImplementation \"${lib.\"spring-context-support\"}\"\n    testImplementation \"${lib.h2}\"\n    testImplementation group: 'com.google.guava', name: 'guava', version: '33.3.1-jre'\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/DynamicTableNameHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.create.index.CreateIndex;\nimport net.sf.jsqlparser.statement.create.view.CreateView;\nimport net.sf.jsqlparser.statement.drop.Drop;\nimport net.sf.jsqlparser.util.TablesNamesFinder;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 动态表名解析处理\n * <p>1.无法保留sql注释(例如 select * from test; --这是个查询 处理完会变成 select * from test)</p>\n * <p>2.无法保留语句分隔符;(例如 select * from test; 处理完会变成 select * from test )</p>\n * <p>3.如果使用转义符包裹了表名需要自行处理</p>\n * <p>4.select * from dual (不处理这个,自行忽略)</p>\n *\n * @author nieqiurong\n * @since 3.5.11\n */\npublic class DynamicTableNameHandler extends TablesNamesFinder<Void> {\n\n    private final String originSql;\n\n    private final TableNameHandler tableNameHandler;\n\n    private final Set<Table> set = new HashSet<>();\n\n    public DynamicTableNameHandler(String originSql, TableNameHandler tableNameHandler) {\n        this.originSql = originSql;\n        this.tableNameHandler = tableNameHandler;\n        init(false);\n    }\n\n    @Override\n    public <S> Void visit(CreateIndex createIndex, S context) {\n        return this.visit(createIndex.getTable(), context);\n    }\n\n    @Override\n    public <S> Void visit(Drop drop, S context) {\n        if(StringUtils.isNotBlank(drop.getType())){\n            String type = drop.getType().toUpperCase();\n            if (\"TABLE\".equals(type)) {\n                return super.visit(drop, context);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public <S> Void visit(CreateView createView, S context) {\n        return super.visit(createView.getSelect(), context);\n    }\n\n    @Override\n    protected String extractTableName(Table table) {\n        String originalTableName = table.getName();\n        if (table.getASTNode() == null) {\n            return originalTableName;\n        }\n        if (set.add(table)) {\n            String tableName = tableNameHandler.dynamicTableName(originSql, originalTableName);\n            if (StringUtils.isNotBlank(tableName)) {\n                table.setName(tableName);\n                return tableName;\n            }\n        }\n        return originalTableName;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserFunction.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport net.sf.jsqlparser.JSQLParserException;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\n@FunctionalInterface\npublic interface JsqlParserFunction<T, R> {\n\n    R apply(T t) throws JSQLParserException;\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserGlobal.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.JsqlParseCache;\nimport com.baomidou.mybatisplus.jsqlparser.JsqlParserThreadPool;\nimport lombok.Setter;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\nimport java.util.concurrent.ExecutorService;\n\n/**\n * @author miemie\n * @since 2023-08-05\n */\npublic class JsqlParserGlobal {\n\n\n    /**\n     * 默认线程数大小\n     *\n     * @since 3.5.6\n     * @deprecated {@link JsqlParserThreadPool#DEFAULT_THREAD_SIZE}\n     */\n    @Deprecated\n    public static final int DEFAULT_THREAD_SIZE = (Runtime.getRuntime().availableProcessors() + 1) / 2;\n\n    /**\n     * 默认解析处理线程池\n     * <p>注意: 由于项目情况,机器配置等不一样因素,请自行根据情况创建指定线程池.</p>\n     *\n     * @see java.util.concurrent.ThreadPoolExecutor\n     * @see #setExecutorService(ExecutorService)\n     * @see #setExecutorService(ExecutorService, boolean)\n     * @since 3.5.6\n     * @deprecated 3.5.11 后面不再公开此属性\n     */\n    @Deprecated\n    public static ExecutorService executorService;\n\n    @Setter\n    private static JsqlParserFunction<String, Statement> parserSingleFunc = sql -> CCJSqlParserUtil.parse(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParserFunction<String, Statements> parserMultiFunc = sql -> CCJSqlParserUtil.parseStatements(sql, getExecutorService(), null);\n\n    @Setter\n    private static JsqlParseCache jsqlParseCache;\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @since 3.5.11\n     */\n    public static void setExecutorService(ExecutorService executorService) {\n        JsqlParserGlobal.executorService = executorService;\n    }\n\n    /**\n     * 设置解析线程池\n     *\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param addShutdownHook 是否注册退出关闭钩子\n     * @since 3.5.11\n     * @deprecated 3.5.12 推荐使用 {@link #setExecutorService(ExecutorService, Thread)}\n     */\n    @Deprecated\n    public static void setExecutorService(ExecutorService executorService, boolean addShutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (addShutdownHook) {\n            JsqlParserThreadPool.addShutdownHook(executorService);\n        }\n    }\n\n    /**\n     * 设置解析线程池\n     * @param executorService 线程池 (自行控制线程池关闭)\n     * @param shutdownHook 关闭钩子\n     * @since 3.5.12\n     */\n    public static void setExecutorService(ExecutorService executorService, Thread shutdownHook) {\n        JsqlParserGlobal.executorService = executorService;\n        if (shutdownHook != null) {\n            Runtime.getRuntime().addShutdownHook(shutdownHook);\n        }\n    }\n    /**\n     * 获取解析线程池(如果未自定义则返回默认的解析线程池)\n     *\n     * @return 解析线程池\n     * @since 3.5.11\n     */\n    public static ExecutorService getExecutorService() {\n        return JsqlParserGlobal.executorService == null ? JsqlParserThreadPool.getDefaultThreadPoolExecutor() : JsqlParserGlobal.executorService;\n    }\n\n    public static Statement parse(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserSingleFunc.apply(sql);\n        }\n        Statement statement = jsqlParseCache.getStatement(sql);\n        if (statement == null) {\n            statement = parserSingleFunc.apply(sql);\n            jsqlParseCache.putStatement(sql, statement);\n        }\n        return statement;\n    }\n\n    public static Statements parseStatements(String sql) throws JSQLParserException {\n        if (jsqlParseCache == null) {\n            return parserMultiFunc.apply(sql);\n        }\n        Statements statements = jsqlParseCache.getStatements(sql);\n        if (statements == null) {\n            statements = parserMultiFunc.apply(sql);\n            jsqlParseCache.putStatements(sql, statements);\n        }\n        return statements;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/JsqlParserSupport.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser;\n\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\n/**\n * https://github.com/JSQLParser/JSqlParser\n *\n * @author miemie\n * @since 2020-06-22\n */\npublic abstract class JsqlParserSupport {\n\n    /**\n     * 日志\n     */\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n    public String parserSingle(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            return processParser(statement, 0, sql, obj);\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    public String parserMulti(String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"original SQL: \" + sql);\n        }\n        try {\n            // fixed github pull/295\n            StringBuilder sb = new StringBuilder();\n            Statements statements = JsqlParserGlobal.parseStatements(sql);\n            int i = 0;\n            for (Statement statement : statements) {\n                if (i > 0) {\n                    sb.append(StringPool.SEMICOLON);\n                }\n                sb.append(processParser(statement, i, sql, obj));\n                i++;\n            }\n            return sb.toString();\n        } catch (JSQLParserException e) {\n            throw ExceptionUtils.mpe(\"Failed to process, Error SQL: %s\", e.getCause(), sql);\n        }\n    }\n\n    /**\n     * 执行 SQL 解析\n     *\n     * @param statement JsqlParser Statement\n     * @return sql\n     */\n    protected String processParser(Statement statement, int index, String sql, Object obj) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"SQL to parse, SQL: \" + sql);\n        }\n        if (statement instanceof Insert) {\n            this.processInsert((Insert) statement, index, sql, obj);\n        } else if (statement instanceof Select) {\n            this.processSelect((Select) statement, index, sql, obj);\n        } else if (statement instanceof Update) {\n            this.processUpdate((Update) statement, index, sql, obj);\n        } else if (statement instanceof Delete) {\n            this.processDelete((Delete) statement, index, sql, obj);\n        }\n        sql = statement.toString();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"parse the finished SQL: \" + sql);\n        }\n        return sql;\n    }\n\n    /**\n     * 新增\n     */\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 删除\n     */\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 更新\n     */\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * 查询\n     */\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/AbstractCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.Executor;\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Caffeine 缓存实现抽象类\n *\n * @author miemie hubin\n * @since 2023-08-08\n */\npublic abstract class AbstractCaffeineJsqlParseCache implements JsqlParseCache {\n    protected final Log logger = LogFactory.getLog(this.getClass());\n    protected final Cache<String, byte[]> cache;\n    @Setter\n    protected boolean async = false;\n    @Setter\n    protected Executor executor;\n\n    public AbstractCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        this.cache = cache;\n    }\n\n    public AbstractCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        Caffeine<Object, Object> caffeine = Caffeine.newBuilder();\n        consumer.accept(caffeine);\n        this.cache = caffeine.build();\n    }\n\n    @Override\n    public void putStatement(String sql, Statement value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public void putStatements(String sql, Statements value) {\n        this.put(sql, value);\n    }\n\n    @Override\n    public Statement getStatement(String sql) {\n        return this.get(sql);\n    }\n\n    @Override\n    public Statements getStatements(String sql) {\n        return this.get(sql);\n    }\n\n    /**\n     * 获取解析对象，异常清空缓存逻辑\n     *\n     * @param sql 执行 SQL\n     * @return 返回泛型对象\n     */\n    @SuppressWarnings(\"unchecked\")\n    protected <T> T get(String sql) {\n        byte[] bytes = cache.getIfPresent(sql);\n        if (null != bytes) {\n            try {\n                return (T) deserialize(sql, bytes);\n            } catch (Exception e) {\n                cache.invalidate(sql);\n                logger.error(\"deserialize error\", e);\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 存储解析对象\n     *\n     * @param sql   执行 SQL\n     * @param value 解析对象\n     */\n    protected void put(String sql, Object value) {\n        final byte[] serialVal = serialize(value);\n        if (async) {\n            if (executor != null) {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal), executor);\n            } else {\n                CompletableFuture.runAsync(() -> cache.put(sql, serialVal));\n            }\n        } else {\n            cache.put(sql, serialVal);\n        }\n    }\n\n    /**\n     * 序列化\n     */\n    public abstract byte[] serialize(Object obj);\n\n    /**\n     * 反序列化\n     */\n    public abstract Object deserialize(String sql, byte[] bytes);\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstFactory.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport org.nustaq.serialization.FSTConfiguration;\n\n/**\n * Fst Factory\n *\n * @author miemie\n * @since 2023-08-06\n */\npublic class FstFactory {\n    private static final FstFactory FACTORY = new FstFactory();\n    private final FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();\n\n    public static FstFactory getDefaultFactory() {\n        return FACTORY;\n    }\n\n    public FstFactory() {\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Alias.AliasColumn.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AllValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnalyticExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.AnyComparisonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ArrayExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CaseExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CastExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.CollateExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ConnectByRootOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateTimeLiteralExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DateValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.DoubleValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.ExtractExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.FilterOverImpl.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Function.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Function.HavingClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.HexValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.IntervalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcNamedParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JdbcParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonAggregateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonFunctionExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.JsonKeyValuePair.class);\n        conf.registerClass(net.sf.jsqlparser.expression.KeepExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LambdaExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.LongValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLGroupConcat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.MySQLIndexHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NextValExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NotExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NullValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.NumericBind.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHierarchicalExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleHint.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OracleNamedFunctionParameter.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OrderByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.OverlapsCondition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.Parenthesis.class);\n        conf.registerClass(net.sf.jsqlparser.expression.PartitionByClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RangeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowConstructor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.RowGetExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SQLServerHints.class);\n        conf.registerClass(net.sf.jsqlparser.expression.SignedExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.StringValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.StructType.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeKeyExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimeValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimestampValue.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TimezoneExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TranscodingFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.TrimFunction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.UserVariable.class);\n        conf.registerClass(net.sf.jsqlparser.expression.VariableAssignment.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WhenClause.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowElement.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowOffset.class);\n        conf.registerClass(net.sf.jsqlparser.expression.WindowRange.class);\n        conf.registerClass(net.sf.jsqlparser.expression.XMLSerializeExpr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Addition.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Concat.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Division.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Modulo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Multiplication.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.arithmetic.Subtraction.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.AndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.OrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.conditional.XorExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Between.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ContainedBy.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Contains.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.DoubleAnd.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.EqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExcludesExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExistsExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.FullTextSearch.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GeometryDistance.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.InExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IncludesExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.IsNullExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.JsonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.LikeExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.Matches.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MemberOfExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThan.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.MinorThanEquals.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NamedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.NotEqualsTo.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.SimilarToExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin.class);\n        conf.registerClass(net.sf.jsqlparser.parser.ASTNodeAccessImpl.class);\n        conf.registerClass(net.sf.jsqlparser.parser.Token.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Column.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Sequence.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Synonym.class);\n        conf.registerClass(net.sf.jsqlparser.schema.Table.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Block.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Commit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DeclareStatement.TypeDefExpr.class);\n        conf.registerClass(net.sf.jsqlparser.statement.DescribeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ExplainStatement.Option.class);\n        conf.registerClass(net.sf.jsqlparser.statement.IfElseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.OutputClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.PurgeStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ReferentialAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ResetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.RollbackStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SavepointStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.SetStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowColumnsStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.ShowStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.Statements.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UnsupportedStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.UseStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.Alter.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropDefault.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSession.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.AlterSystemStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.RenameTableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.alter.sequence.AlterSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.analyze.Analyze.class);\n        conf.registerClass(net.sf.jsqlparser.statement.comment.Comment.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.function.CreateFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.index.CreateIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.procedure.CreateProcedure.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.schema.CreateSchema.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.sequence.CreateSequence.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.synonym.CreateSynonym.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CheckConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColDataType.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ColumnDefinition.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.CreateTable.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ExcludeConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.Index.ColumnParams.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.NamedConstraint.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.table.RowMovement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.AlterView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.create.view.CreateView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.delete.Delete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.drop.Drop.class);\n        conf.registerClass(net.sf.jsqlparser.statement.execute.Execute.class);\n        conf.registerClass(net.sf.jsqlparser.statement.grant.Grant.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.Insert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictAction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.insert.InsertConflictTarget.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.Merge.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeDelete.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeInsert.class);\n        conf.registerClass(net.sf.jsqlparser.statement.merge.MergeUpdate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.AllTableColumns.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Distinct.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ExceptOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Fetch.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.First.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ForClause.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.GroupByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.IntersectOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Join.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLJoinWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.KSQLWindow.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralSubSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.LateralView.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Limit.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.MinusOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Offset.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OptimizeFor.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.OrderByElement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedFromItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.ParenthesedSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Pivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PivotXml.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.PlainSelect.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SelectItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperationList.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Skip.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableFunction.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.TableStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Top.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnPivot.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.UnionOp.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Values.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Wait.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithIsolation.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.WithItem.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowIndexStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.show.ShowTablesStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.truncate.Truncate.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.Update.class);\n        conf.registerClass(net.sf.jsqlparser.statement.update.UpdateSet.class);\n        conf.registerClass(net.sf.jsqlparser.statement.upsert.Upsert.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiAndExpression.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultiOrExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.BinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.ComparisonOperator.class);\n        conf.registerClass(net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression.class);\n        conf.registerClass(net.sf.jsqlparser.statement.CreateFunctionalStatement.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.Select.class);\n        conf.registerClass(net.sf.jsqlparser.statement.select.SetOperation.class);\n        conf.registerClass(net.sf.jsqlparser.util.cnfexpression.MultipleExpression.class);\n    }\n\n    public byte[] asByteArray(Object obj) {\n        return conf.asByteArray(obj);\n    }\n\n    public Object asObject(byte[] bytes) {\n        return conf.asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/FstSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 Fst 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class FstSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public FstSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public FstSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return FstFactory.getDefaultFactory().asByteArray(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return FstFactory.getDefaultFactory().asObject(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JdkSerialCaffeineJsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport com.baomidou.mybatisplus.core.toolkit.SerializationUtils;\nimport com.github.benmanes.caffeine.cache.Cache;\nimport com.github.benmanes.caffeine.cache.Caffeine;\n\nimport java.util.function.Consumer;\n\n/**\n * jsqlparser 缓存 jdk 序列化 Caffeine 缓存实现\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic class JdkSerialCaffeineJsqlParseCache extends AbstractCaffeineJsqlParseCache {\n\n\n    public JdkSerialCaffeineJsqlParseCache(Cache<String, byte[]> cache) {\n        super(cache);\n    }\n\n    public JdkSerialCaffeineJsqlParseCache(Consumer<Caffeine<Object, Object>> consumer) {\n        super(consumer);\n    }\n\n    @Override\n    public byte[] serialize(Object obj) {\n        return SerializationUtils.serialize(obj);\n    }\n\n    @Override\n    public Object deserialize(String sql, byte[] bytes) {\n        return SerializationUtils.deserialize(bytes);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/parser/cache/JsqlParseCache.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.parser.cache;\n\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.Statements;\n\n/**\n * jsqlparser 缓存接口\n *\n * @author miemie\n * @since 2023-08-05\n */\npublic interface JsqlParseCache {\n\n    void putStatement(String sql, Statement value);\n\n    void putStatements(String sql, Statements value);\n\n    Statement getStatement(String sql);\n\n    Statements getStatements(String sql);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/DataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.4.1 +\n */\npublic interface DataPermissionHandler {\n\n    /**\n     * 获取数据权限 SQL 片段\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会覆盖原有的条件表达式\n     */\n    Expression getSqlSegment(Expression where, String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/MultiDataPermissionHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\n\n/**\n * 支持多表的数据权限处理器\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic interface MultiDataPermissionHandler extends DataPermissionHandler {\n    /**\n     * 为兼容旧版数据权限处理器，继承了 {@link DataPermissionHandler} 但是新的多表数据权限处理又不会调用此方法，因此标记过时\n     *\n     * @param where             待执行 SQL Where 条件表达式\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式\n     * @deprecated 新的多表数据权限处理不会调用此方法，因此标记过时\n     */\n    @Deprecated\n    @Override\n    default Expression getSqlSegment(Expression where, String mappedStatementId) {\n        return where;\n    }\n\n    /**\n     * 获取数据权限 SQL 片段。\n     * <p>旧的 {@link MultiDataPermissionHandler#getSqlSegment(Expression, String)} 方法第一个参数包含所有的 where 条件信息，如果 return 了 null 会覆盖原有的 where 数据，</p>\n     * <p>新版的 {@link MultiDataPermissionHandler#getSqlSegment(Table, Expression, String)} 方法不能覆盖原有的 where 数据，如果 return 了 null 则表示不追加任何 where 条件</p>\n     *\n     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名\n     * @param where             原有的 where 条件信息\n     * @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法\n     * @return JSqlParser 条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）\n     */\n    Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/handler/TenantLineHandler.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.handler;\n\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\n\nimport java.util.List;\n\n/**\n * 租户处理器（ TenantId 行级 ）\n *\n * @author hubin\n * @since 3.4.0\n */\npublic interface TenantLineHandler {\n\n    /**\n     * 获取租户 ID 值表达式，只支持单个 ID 值\n     * <p>\n     *\n     * @return 租户 ID 值表达式\n     */\n    Expression getTenantId();\n\n    /**\n     * 获取租户字段名\n     * <p>\n     * 默认字段名叫: tenant_id\n     *\n     * @return 租户字段名\n     */\n    default String getTenantIdColumn() {\n        return \"tenant_id\";\n    }\n\n    /**\n     * 根据表名判断是否忽略拼接多租户条件\n     * <p>\n     * 默认都要进行解析并拼接多租户条件\n     *\n     * @param tableName 表名\n     * @return 是否忽略, true:表示忽略，false:需要解析并拼接多租户条件\n     */\n    default boolean ignoreTable(String tableName) {\n        return false;\n    }\n\n    /**\n     * 忽略插入租户字段逻辑\n     *\n     * @param columns        插入字段\n     * @param tenantIdColumn 租户 ID 字段\n     * @return\n     */\n    default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {\n        return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BaseMultiTableInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.*;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExistsExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 多表条件处理基对象，从原有的 {@link TenantLineInnerInterceptor} 拦截器中提取出来\n *\n * @author houkunlin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 条件表达式追加模式 (默认放置最后,仅作用于update,delete,select)\n     *\n     * @since 3.5.11\n     */\n    private ExpressionAppendMode expressionAppendMode = ExpressionAppendMode.LAST;\n\n    protected void processSelectBody(Select selectBody, final String whereSegment) {\n        if (selectBody == null) {\n            return;\n        }\n        if (selectBody instanceof PlainSelect) {\n            processPlainSelect((PlainSelect) selectBody, whereSegment);\n        } else if (selectBody instanceof ParenthesedSelect) {\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processSelectBody(parenthesedSelect.getSelect(), whereSegment);\n        } else if (selectBody instanceof SetOperationList) {\n            SetOperationList operationList = (SetOperationList) selectBody;\n            List<Select> selectBodyList = operationList.getSelects();\n            if (CollectionUtils.isNotEmpty(selectBodyList)) {\n                selectBodyList.forEach(body -> processSelectBody(body, whereSegment));\n            }\n        }\n    }\n\n    /**\n     * delete update 语句 where 处理\n     */\n    protected Expression andExpression(Table table, Expression where, final String whereSegment) {\n        //获得where条件表达式\n        final Expression expression = buildTableExpression(table, where, whereSegment);\n        if (expression == null) {\n            return where;\n        }\n        if (where != null) {\n            if (where instanceof OrExpression) {\n                return appendExpression(new ParenthesedExpressionList<>(where), expression);\n            } else {\n                return appendExpression(where, expression);\n            }\n        }\n        return expression;\n    }\n\n    /**\n     * 处理 PlainSelect\n     */\n    protected void processPlainSelect(final PlainSelect plainSelect, final String whereSegment) {\n        //#3087 github\n        List<SelectItem<?>> selectItems = plainSelect.getSelectItems();\n        if (CollectionUtils.isNotEmpty(selectItems)) {\n            selectItems.forEach(selectItem -> processSelectItem(selectItem, whereSegment));\n        }\n\n        // 处理 where 中的子查询\n        Expression where = plainSelect.getWhere();\n        processWhereSubSelect(where, whereSegment);\n\n        // 处理 fromItem\n        FromItem fromItem = plainSelect.getFromItem();\n        List<Table> list = processFromItem(fromItem, whereSegment);\n        List<Table> mainTables = new ArrayList<>(list);\n\n        // 处理 join\n        List<Join> joins = plainSelect.getJoins();\n        if (CollectionUtils.isNotEmpty(joins)) {\n            processJoins(mainTables, joins, whereSegment);\n        }\n\n        // 当有 mainTable 时，进行 where 条件追加\n        if (CollectionUtils.isNotEmpty(mainTables)) {\n            plainSelect.setWhere(builderExpression(where, mainTables, whereSegment));\n        }\n    }\n\n    private List<Table> processFromItem(FromItem fromItem, final String whereSegment) {\n        // 处理括号括起来的表达式\n//        while (fromItem instanceof ParenthesedFromItem) {\n//            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n//        }\n\n        List<Table> mainTables = new ArrayList<>();\n        // 无 join 时的处理逻辑\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            mainTables.add(fromTable);\n        } else if (fromItem instanceof ParenthesedFromItem) {\n            // SubJoin 类型则还需要添加上 where 条件\n            List<Table> tables = processSubJoin((ParenthesedFromItem) fromItem, whereSegment);\n            mainTables.addAll(tables);\n        } else {\n            // 处理下 fromItem\n            processOtherFromItem(fromItem, whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理where条件内的子查询\n     * <p>\n     * 支持如下:\n     * <ol>\n     *     <li>in</li>\n     *     <li>=</li>\n     *     <li>&gt;</li>\n     *     <li>&lt;</li>\n     *     <li>&gt;=</li>\n     *     <li>&lt;=</li>\n     *     <li>&lt;&gt;</li>\n     *     <li>EXISTS</li>\n     *     <li>NOT EXISTS</li>\n     * </ol>\n     * <p>\n     * 前提条件:\n     * 1. 子查询必须放在小括号中\n     * 2. 子查询一般放在比较操作符的右边\n     *\n     * @param where where 条件\n     */\n    protected void processWhereSubSelect(Expression where, final String whereSegment) {\n        if (where == null) {\n            return;\n        }\n        if (where instanceof FromItem) {\n            processOtherFromItem((FromItem) where, whereSegment);\n            return;\n        }\n        if (where.toString().indexOf(\"SELECT\") > 0) {\n            // 有子查询\n            if (where instanceof BinaryExpression) {\n                // 比较符号 , and , or , 等等\n                BinaryExpression expression = (BinaryExpression) where;\n                processWhereSubSelect(expression.getLeftExpression(), whereSegment);\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof InExpression) {\n                // in\n                InExpression expression = (InExpression) where;\n                Expression inExpression = expression.getRightExpression();\n                if (inExpression instanceof Select) {\n                    processSelectBody(((Select) inExpression), whereSegment);\n                }\n            } else if (where instanceof ExistsExpression) {\n                // exists\n                ExistsExpression expression = (ExistsExpression) where;\n                processWhereSubSelect(expression.getRightExpression(), whereSegment);\n            } else if (where instanceof NotExpression) {\n                // not exists\n                NotExpression expression = (NotExpression) where;\n                processWhereSubSelect(expression.getExpression(), whereSegment);\n            } else if (where instanceof ParenthesedExpressionList) {\n                ParenthesedExpressionList<Expression> expression = (ParenthesedExpressionList) where;\n                processWhereSubSelect(expression.get(0), whereSegment);\n            }\n        }\n    }\n\n    protected void processSelectItem(SelectItem selectItem, final String whereSegment) {\n        Expression expression = selectItem.getExpression();\n        if (expression instanceof Select) {\n            processSelectBody(((Select) expression), whereSegment);\n        } else if (expression instanceof Function) {\n            processFunction((Function) expression, whereSegment);\n        } else if (expression instanceof ExistsExpression) {\n            ExistsExpression existsExpression = (ExistsExpression) expression;\n            processSelectBody((Select) existsExpression.getRightExpression(), whereSegment);\n        }\n    }\n\n    /**\n     * 处理函数\n     * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>\n     * <p> fixed gitee pulls/141</p>\n     *\n     * @param function\n     */\n    protected void processFunction(Function function, final String whereSegment) {\n        ExpressionList<?> parameters = function.getParameters();\n        if (parameters != null) {\n            parameters.forEach(expression -> {\n                if (expression instanceof Select) {\n                    processSelectBody(((Select) expression), whereSegment);\n                } else if (expression instanceof Function) {\n                    processFunction((Function) expression, whereSegment);\n                } else if (expression instanceof EqualsTo) {\n                    if (((EqualsTo) expression).getLeftExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getLeftExpression()), whereSegment);\n                    }\n                    if (((EqualsTo) expression).getRightExpression() instanceof Select) {\n                        processSelectBody(((Select) ((EqualsTo) expression).getRightExpression()), whereSegment);\n                    }\n                }\n            });\n        }\n    }\n\n    /**\n     * 处理子查询等\n     */\n    protected void processOtherFromItem(FromItem fromItem, final String whereSegment) {\n        // 去除括号\n        while (fromItem instanceof ParenthesedFromItem) {\n            fromItem = ((ParenthesedFromItem) fromItem).getFromItem();\n        }\n\n        if (fromItem instanceof ParenthesedSelect) {\n            Select subSelect = (Select) fromItem;\n            processSelectBody(subSelect, whereSegment);\n        }\n    }\n\n    /**\n     * 处理 sub join\n     *\n     * @param subJoin subJoin\n     * @return Table subJoin 中的主表\n     */\n    private List<Table> processSubJoin(ParenthesedFromItem subJoin, final String whereSegment) {\n        while (subJoin.getJoins() == null && subJoin.getFromItem() instanceof ParenthesedFromItem) {\n            subJoin = (ParenthesedFromItem) subJoin.getFromItem();\n        }\n        List<Table> tableList = processFromItem(subJoin.getFromItem(), whereSegment);\n        List<Table> mainTables = new ArrayList<>(tableList);\n        if (subJoin.getJoins() != null) {\n            processJoins(mainTables, subJoin.getJoins(), whereSegment);\n        }\n        return mainTables;\n    }\n\n    /**\n     * 处理 joins\n     *\n     * @param mainTables 可以为 null\n     * @param joins      join 集合\n     * @return List<Table> 右连接查询的 Table 列表\n     */\n    private List<Table> processJoins(List<Table> mainTables, List<Join> joins, final String whereSegment) {\n        // join 表达式中最终的主表\n        Table mainTable = null;\n        // 当前 join 的左表\n        Table leftTable = null;\n\n        if (mainTables.size() == 1) {\n            mainTable = mainTables.get(0);\n            leftTable = mainTable;\n        }\n\n        //对于 on 表达式写在最后的 join，需要记录下前面多个 on 的表名\n        Deque<List<Table>> onTableDeque = new LinkedList<>();\n        for (Join join : joins) {\n            // 处理 on 表达式\n            FromItem joinItem = join.getRightItem();\n\n            // 获取当前 join 的表，subJoint 可以看作是一张表\n            List<Table> joinTables = null;\n            if (joinItem instanceof Table) {\n                joinTables = new ArrayList<>();\n                joinTables.add((Table) joinItem);\n            } else if (joinItem instanceof ParenthesedFromItem) {\n                joinTables = processSubJoin((ParenthesedFromItem) joinItem, whereSegment);\n            }\n\n            if (joinTables != null && !joinTables.isEmpty()) {\n\n                // 如果是隐式内连接\n                if (join.isSimple()) {\n                    mainTables.addAll(joinTables);\n                    continue;\n                }\n\n                // 当前表是否忽略\n                Table joinTable = joinTables.get(0);\n\n                List<Table> onTables = null;\n                // 如果不要忽略，且是右连接，则记录下当前表\n                if (join.isRight()) {\n                    mainTable = joinTable;\n                    mainTables.clear();\n                    if (leftTable != null) {\n                        onTables = Collections.singletonList(leftTable);\n                    }\n                } else if (join.isInner()) {\n                    if (mainTable == null) {\n                        onTables = Collections.singletonList(joinTable);\n                    } else {\n                        onTables = Arrays.asList(mainTable, joinTable);\n                    }\n                    mainTable = null;\n                    mainTables.clear();\n                } else {\n                    onTables = Collections.singletonList(joinTable);\n                }\n\n                if (mainTable != null && !mainTables.contains(mainTable)) {\n                    mainTables.add(mainTable);\n                }\n\n                // 获取 join 尾缀的 on 表达式列表\n                Collection<Expression> originOnExpressions = join.getOnExpressions();\n                // 正常 join on 表达式只有一个，立刻处理\n                if (originOnExpressions.size() == 1 && onTables != null) {\n                    List<Expression> onExpressions = new LinkedList<>();\n                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables, whereSegment));\n                    join.setOnExpressions(onExpressions);\n                    leftTable = mainTable == null ? joinTable : mainTable;\n                    continue;\n                }\n                // 表名压栈，忽略的表压入 null，以便后续不处理\n                onTableDeque.push(onTables);\n                // 尾缀多个 on 表达式的时候统一处理\n                if (originOnExpressions.size() > 1) {\n                    Collection<Expression> onExpressions = new LinkedList<>();\n                    for (Expression originOnExpression : originOnExpressions) {\n                        List<Table> currentTableList = onTableDeque.poll();\n                        if (CollectionUtils.isEmpty(currentTableList)) {\n                            onExpressions.add(originOnExpression);\n                        } else {\n                            onExpressions.add(builderExpression(originOnExpression, currentTableList, whereSegment));\n                        }\n                    }\n                    join.setOnExpressions(onExpressions);\n                }\n                leftTable = joinTable;\n            } else {\n                processOtherFromItem(joinItem, whereSegment);\n                leftTable = null;\n            }\n        }\n\n        return mainTables;\n    }\n\n    /**\n     * 处理条件\n     */\n    protected Expression builderExpression(Expression currentExpression, List<Table> tables, final String whereSegment) {\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(tables)) {\n            return currentExpression;\n        }\n        // 构造每张表的条件\n        List<Expression> expressions = tables.stream()\n            .map(item -> buildTableExpression(item, currentExpression, whereSegment))\n            .filter(Objects::nonNull)\n            .collect(Collectors.toList());\n\n        // 没有表需要处理直接返回\n        if (CollectionUtils.isEmpty(expressions)) {\n            return currentExpression;\n        }\n\n        // 注入的表达式\n        Expression injectExpression = expressions.get(0);\n        // 如果有多表，则用 and 连接\n        if (expressions.size() > 1) {\n            for (int i = 1; i < expressions.size(); i++) {\n                injectExpression = new AndExpression(injectExpression, expressions.get(i));\n            }\n        }\n\n        if (currentExpression == null) {\n            return injectExpression;\n        }\n        if (currentExpression instanceof OrExpression) {\n            return appendExpression(new ParenthesedExpressionList<>(currentExpression), injectExpression);\n        } else {\n            return appendExpression(currentExpression, injectExpression);\n        }\n    }\n\n    /**\n     * 追加表达式，默认追加到后面，可以配置变量 {@link #expressionAppendMode} 来控制追加到前面还是后面\n     *\n     * @param currentExpression 原sql的条件表达式\n     * @param injectExpression  注入的表达式\n     * @return 追加了条件的完整表达式(where条件 / on条件)\n     * @since 3.5.11\n     */\n    protected Expression appendExpression(Expression currentExpression, Expression injectExpression) {\n        if (ExpressionAppendMode.LAST == expressionAppendMode || expressionAppendMode == null) {\n            return new AndExpression(currentExpression, injectExpression);\n        } else {\n            return new AndExpression(injectExpression, currentExpression);\n        }\n    }\n\n    /**\n     * 构建数据库表的查询条件\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径\n     * @return 需要拼接的新条件（不会覆盖原有的where条件，只会在原有条件上再加条件），为 null 则不加入新的条件\n     */\n    public abstract Expression buildTableExpression(final Table table, final Expression where, final String whereSegment);\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/BlockAttackInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.IsNullExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\n\n/**\n * 攻击 SQL 阻断解析器,防止全表更新与删除\n *\n * @author hubin\n * @since 3.4.0\n */\npublic class BlockAttackInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler handler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = handler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreBlockAttack(ms.getId())) {\n                return;\n            }\n            BoundSql boundSql = handler.boundSql();\n            parserMulti(boundSql.getSql(), null);\n        }\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        this.checkWhere(delete.getTable().getName(), delete.getWhere(), \"Prohibition of full table deletion\");\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        this.checkWhere(update.getTable().getName(), update.getWhere(), \"Prohibition of table update operation\");\n    }\n\n    protected void checkWhere(String tableName, Expression where, String ex) {\n        Assert.isFalse(this.fullMatch(where, this.getTableLogicField(tableName)), ex);\n    }\n\n    private boolean fullMatch(Expression where, String logicField) {\n        if (where == null) {\n            return true;\n        }\n        if (StringUtils.isNotBlank(logicField)) {\n\n            if (where instanceof BinaryExpression) {\n                BinaryExpression binaryExpression = (BinaryExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField) || StringUtils.equals(binaryExpression.getRightExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n\n            if (where instanceof IsNullExpression) {\n                IsNullExpression binaryExpression = (IsNullExpression) where;\n                if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField)) {\n                    return true;\n                }\n            }\n        }\n\n        if (where instanceof EqualsTo) {\n            // example: 1=1\n            EqualsTo equalsTo = (EqualsTo) where;\n            return StringUtils.equals(equalsTo.getLeftExpression().toString(), equalsTo.getRightExpression().toString());\n        } else if (where instanceof NotEqualsTo) {\n            // example: 1 != 2\n            NotEqualsTo notEqualsTo = (NotEqualsTo) where;\n            return !StringUtils.equals(notEqualsTo.getLeftExpression().toString(), notEqualsTo.getRightExpression().toString());\n        } else if (where instanceof OrExpression) {\n\n            OrExpression orExpression = (OrExpression) where;\n            return fullMatch(orExpression.getLeftExpression(), logicField) || fullMatch(orExpression.getRightExpression(), logicField);\n        } else if (where instanceof AndExpression) {\n\n            AndExpression andExpression = (AndExpression) where;\n            return fullMatch(andExpression.getLeftExpression(), logicField) && fullMatch(andExpression.getRightExpression(), logicField);\n        } else if (where instanceof ParenthesedExpressionList) {\n            // example: (1 = 1)\n            ParenthesedExpressionList<Expression> parenthesis = (ParenthesedExpressionList<Expression>) where;\n            return fullMatch(parenthesis.get(0), logicField);\n        }\n\n        return false;\n    }\n\n    /**\n     * 获取表名中的逻辑删除字段\n     *\n     * @param tableName 表名\n     * @return 逻辑删除字段\n     */\n    private String getTableLogicField(String tableName) {\n        if (StringUtils.isBlank(tableName)) {\n            return StringPool.EMPTY;\n        }\n\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);\n        if (tableInfo == null || !tableInfo.isWithLogicDelete() || tableInfo.getLogicDeleteFieldInfo() == null) {\n            return StringPool.EMPTY;\n        }\n        return tableInfo.getLogicDeleteFieldInfo().getColumn();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataChangeRecorderInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.io.Reader;\nimport java.sql.Clob;\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.ResultSetMetaData;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport net.sf.jsqlparser.statement.select.Values;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.reflection.MetaObject;\nimport org.apache.ibatis.reflection.SystemMetaObject;\nimport org.apache.ibatis.scripting.defaults.DefaultParameterHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.baomidou.mybatisplus.annotation.IEnum;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;\nimport com.baomidou.mybatisplus.core.metadata.TableFieldInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\n\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.JdbcParameter;\nimport net.sf.jsqlparser.expression.RowConstructor;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.AllColumns;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SelectItem;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\n\n/**\n * <p>\n * 数据变动记录插件\n * 默认会生成一条log，格式：\n * ----------------------INSERT LOG------------------------------\n * </p>\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"insert\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\",\n * \"TEST_ID\": \"null->1561666810058739714\",\n * \"AGE\": \"null->THREE\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n * <p>\n * * ----------------------UPDATE LOG------------------------------\n * <p>\n * {\n * \"tableName\": \"h2user\",\n * \"operation\": \"update\",\n * \"recordStatus\": \"true\",\n * \"changedData\": [\n * {\n * \"TEST_ID\": \"102\",\n * \"AGE\": \"2->THREE\",\n * \"FIRSTNAME\": \"DOU.HAO->{\\\"json\\\":\\\"abc\\\"}\",\n * \"LAST_UPDATED_DT\": \"null->2022-08-22 18:49:16.512\"\n * }\n * ],\n * \"cost(ms)\": 0\n * }\n * </p>\n *\n * @author yuxiaobin\n * @deprecated 3.5.10 问题太多,计划移除\n * @date 2022-8-21\n */\n@Deprecated\npublic class DataChangeRecorderInnerInterceptor implements InnerInterceptor {\n\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n    @SuppressWarnings(\"unused\")\n    public static final String IGNORED_TABLE_COLUMN_PROPERTIES = \"ignoredTableColumns\";\n\n    private final Map<String, Set<String>> ignoredTableColumns = new ConcurrentHashMap<>();\n    private final Set<String> ignoreAllColumns = new HashSet<>();//全部表的这些字段名，INSERT/UPDATE都忽略，delete暂时保留\n    //批量更新上限, 默认一次最多1000条\n    private int BATCH_UPDATE_LIMIT = 1000;\n    private boolean batchUpdateLimitationOpened = false;\n    private final Map<String, Integer> BATCH_UPDATE_LIMIT_MAP = new ConcurrentHashMap<>();//表名->批量更新上限\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        final BoundSql boundSql = mpSh.boundSql();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            OperationResult operationResult;\n            long startTs = System.currentTimeMillis();\n            try {\n                Statement statement = JsqlParserGlobal.parse(mpBs.sql());\n                if (statement instanceof Insert) {\n                    operationResult = processInsert((Insert) statement, mpSh.boundSql());\n                } else if (statement instanceof Update) {\n                    operationResult = processUpdate((Update) statement, ms, boundSql, connection);\n                } else if (statement instanceof Delete) {\n                    operationResult = processDelete((Delete) statement, ms, boundSql, connection);\n                } else {\n                    logger.info(\"other operation sql={}\", mpBs.sql());\n                    return;\n                }\n            } catch (Exception e) {\n                if (e instanceof DataUpdateLimitationException) {\n                    throw (DataUpdateLimitationException) e;\n                }\n                logger.error(\"Unexpected error for mappedStatement={}, sql={}\", ms.getId(), mpBs.sql(), e);\n                return;\n            }\n            long costThis = System.currentTimeMillis() - startTs;\n            if (operationResult != null) {\n                operationResult.setCost(costThis);\n                dealOperationResult(operationResult);\n            }\n        }\n    }\n\n    /**\n     * 判断哪些SQL需要处理\n     * 默认INSERT/UPDATE/DELETE语句\n     *\n     * @param sql\n     * @return\n     */\n    protected boolean allowProcess(String sql) {\n        String sqlTrim = sql.trim().toUpperCase();\n        return sqlTrim.startsWith(\"INSERT\") || sqlTrim.startsWith(\"UPDATE\") || sqlTrim.startsWith(\"DELETE\");\n    }\n\n    /**\n     * 处理数据更新结果，默认打印\n     *\n     * @param operationResult\n     */\n    protected void dealOperationResult(OperationResult operationResult) {\n        logger.info(\"{}\", operationResult);\n    }\n\n    public OperationResult processInsert(Insert insertStmt, BoundSql boundSql) {\n        String operation = SqlCommandType.INSERT.name().toLowerCase();\n        Table table = insertStmt.getTable();\n        String tableName = table.getName();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, insertStmt);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), null, updatedColumnDatas));\n        return result;\n    }\n\n    public OperationResult processUpdate(Update updateStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Expression where = updateStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        Table table = updateStmt.getTable();\n        String tableName = table.getName();\n        String operation = SqlCommandType.UPDATE.name().toLowerCase();\n        Optional<OperationResult> optionalOperationResult = ignoredTableColumns(tableName, operation);\n        if (optionalOperationResult.isPresent()) {\n            return optionalOperationResult.get();\n        }\n        selectBody.setFromItem(table);\n        List<Column> updateColumns = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateColumns.addAll(updateSet.getColumns());\n        }\n        Columns2SelectItemsResult buildColumns2SelectItems = buildColumns2SelectItems(tableName, updateColumns);\n        selectBody.setSelectItems(buildColumns2SelectItems.getSelectItems());\n        selectBody.setWhere(where);\n        SelectItem<PlainSelect> plainSelectSelectItem = new SelectItem<>(selectBody);\n\n        BoundSql boundSql4Select = new BoundSql(mappedStatement.getConfiguration(), plainSelectSelectItem.toString(),\n            prepareParameterMapping4Select(boundSql.getParameterMappings(), updateStmt),\n            boundSql.getParameterObject());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n        Map<String, Object> additionalParameters = mpBoundSql.additionalParameters();\n        if (additionalParameters != null && !additionalParameters.isEmpty()) {\n            for (Map.Entry<String, Object> ety : additionalParameters.entrySet()) {\n                boundSql4Select.setAdditionalParameter(ety.getKey(), ety.getValue());\n            }\n        }\n        Map<String, Object> updatedColumnDatas = getUpdatedColumnDatas(tableName, boundSql, updateStmt);\n        OriginalDataObj originalData = buildOriginalObjectData(updatedColumnDatas, selectBody, buildColumns2SelectItems.getPk(), mappedStatement, boundSql4Select, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(operation);\n        result.setTableName(tableName);\n        result.setRecordStatus(true);\n        result.buildDataStr(compareAndGetUpdatedColumnDatas(result.getTableName(), originalData, updatedColumnDatas));\n        return result;\n    }\n\n    private Optional<OperationResult> ignoredTableColumns(String table, String operation) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(table.toUpperCase());\n        if (ignoredColumns != null) {\n            if (ignoredColumns.stream().anyMatch(\"*\"::equals)) {\n                OperationResult result = new OperationResult();\n                result.setOperation(operation);\n                result.setTableName(table + \":*\");\n                result.setRecordStatus(false);\n                return Optional.of(result);\n            }\n        }\n        return Optional.empty();\n    }\n\n    private TableInfo getTableInfoByTableName(String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                return tableInfo;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 将update SET部分的jdbc参数去除\n     *\n     * @param originalMappingList 这里只会包含JdbcParameter参数\n     * @param updateStmt\n     * @return\n     */\n    private List<ParameterMapping> prepareParameterMapping4Select(List<ParameterMapping> originalMappingList, Update updateStmt) {\n        List<Expression> updateValueExpressions = new ArrayList<>();\n        for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n            updateValueExpressions.addAll(updateSet.getValues());\n        }\n        int removeParamCount = 0;\n        for (Expression expression : updateValueExpressions) {\n            if (expression instanceof JdbcParameter) {\n                ++removeParamCount;\n            }\n        }\n        return originalMappingList.subList(removeParamCount, originalMappingList.size());\n    }\n\n    protected Map<String, Object> getUpdatedColumnDatas(String tableName, BoundSql updateSql, Statement statement) {\n        Map<String, Object> columnNameValMap = new HashMap<>(updateSql.getParameterMappings().size());\n        Map<Integer, String> columnSetIndexMap = new HashMap<>(updateSql.getParameterMappings().size());\n        List<Column> selectItemsFromUpdateSql = new ArrayList<>();\n        if (statement instanceof Update) {\n            Update updateStmt = (Update) statement;\n            int index = 0;\n            for (UpdateSet updateSet : updateStmt.getUpdateSets()) {\n                selectItemsFromUpdateSql.addAll(updateSet.getColumns());\n                final ExpressionList<Expression> updateList = (ExpressionList<Expression>) updateSet.getValues();\n                for (int i = 0; i < updateList.size(); ++i) {\n                    Expression updateExps = updateList.get(i);\n                    if (!(updateExps instanceof JdbcParameter)) {\n                        columnNameValMap.put(updateSet.getColumns().get(i).getColumnName().toUpperCase(), updateExps.toString());\n                    }\n                    columnSetIndexMap.put(index++, updateSet.getColumns().get(i).getColumnName().toUpperCase());\n                }\n            }\n        } else if (statement instanceof Insert) {\n            Insert insert = (Insert) statement;\n            selectItemsFromUpdateSql.addAll(insert.getColumns());\n            columnNameValMap.putAll(detectInsertColumnValuesNonJdbcParameters(insert));\n        }\n        Map<String, String> relatedColumnsUpperCaseWithoutUnderline = new HashMap<>(selectItemsFromUpdateSql.size(), 1);\n        for (Column item : selectItemsFromUpdateSql) {\n            //FIRSTNAME: FIRST_NAME/FIRST-NAME/FIRST$NAME/FIRST.NAME\n            relatedColumnsUpperCaseWithoutUnderline.put(item.getColumnName().replaceAll(\"[._\\\\-$]\", \"\").toUpperCase(), item.getColumnName().toUpperCase());\n        }\n        MetaObject metaObject = SystemMetaObject.forObject(updateSql.getParameterObject());\n        int index = 0;\n        for (ParameterMapping parameterMapping : updateSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                ++index;\n                continue;\n            }\n            String[] arr = propertyName.split(\"\\\\.\");\n            String propertyNameTrim = arr[arr.length - 1].replace(\"_\", \"\").toUpperCase();\n            try {\n                final String columnName = columnSetIndexMap.getOrDefault(index++, getColumnNameByProperty(propertyNameTrim, tableName));\n                if (relatedColumnsUpperCaseWithoutUnderline.containsKey(propertyNameTrim)) {\n                    final String colkey = relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim);\n                    Object valObj = metaObject.getValue(propertyName);\n                    if (valObj instanceof IEnum) {\n                        valObj = ((IEnum<?>) valObj).getValue();\n                    } else if (valObj instanceof Enum) {\n                        valObj = getEnumValue((Enum) valObj);\n                    }\n                    if (columnNameValMap.containsKey(colkey)) {\n                        columnNameValMap.put(relatedColumnsUpperCaseWithoutUnderline.get(propertyNameTrim), String.valueOf(columnNameValMap.get(colkey)).replace(\"?\", valObj == null ? \"\" : valObj.toString()));\n                    }\n                    if (columnName != null && !columnNameValMap.containsKey(columnName)) {\n                        columnNameValMap.put(columnName, valObj);\n                    }\n                } else {\n                    if (columnName != null) {\n                        columnNameValMap.put(columnName, metaObject.getValue(propertyName));\n                    }\n                }\n            } catch (Exception e) {\n                logger.warn(\"get value error,propertyName:{},parameterMapping:{}\", propertyName, parameterMapping);\n            }\n        }\n        dealWithUpdateWrapper(columnSetIndexMap, columnNameValMap, updateSql);\n        return columnNameValMap;\n    }\n\n    /**\n     * @param originalDataObj\n     * @return\n     */\n    private List<DataChangedRecord> compareAndGetUpdatedColumnDatas(String tableName, OriginalDataObj originalDataObj, Map<String, Object> columnNameValMap) {\n        final Set<String> ignoredColumns = ignoredTableColumns.get(tableName.toUpperCase());\n        if (originalDataObj == null || originalDataObj.isEmpty()) {\n            DataChangedRecord oneRecord = new DataChangedRecord();\n            List<DataColumnChangeResult> updateColumns = new ArrayList<>(columnNameValMap.size());\n            for (Map.Entry<String, Object> ety : columnNameValMap.entrySet()) {\n                String columnName = ety.getKey();\n                if ((ignoredColumns == null || !ignoredColumns.contains(columnName)) && !ignoreAllColumns.contains(columnName)) {\n                    updateColumns.add(DataColumnChangeResult.constrcutByUpdateVal(columnName, ety.getValue()));\n                }\n            }\n            oneRecord.setUpdatedColumns(updateColumns);\n//            oneRecord.setUpdatedColumns(Collections.EMPTY_LIST);\n            return Collections.singletonList(oneRecord);\n        }\n        List<DataChangedRecord> originalDataList = originalDataObj.getOriginalDataObj();\n        List<DataChangedRecord> updateDataList = new ArrayList<>(originalDataList.size());\n        for (DataChangedRecord originalData : originalDataList) {\n            if (originalData.hasUpdate(columnNameValMap, ignoredColumns, ignoreAllColumns)) {\n                updateDataList.add(originalData);\n            }\n        }\n        return updateDataList;\n    }\n\n    private Object getEnumValue(Enum enumVal) {\n        Optional<String> enumValueFieldName = MybatisEnumTypeHandler.findEnumValueFieldName(enumVal.getClass());\n        if (enumValueFieldName.isPresent()) {\n            return SystemMetaObject.forObject(enumVal).getValue(enumValueFieldName.get());\n        }\n        return enumVal;\n\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n    private void dealWithUpdateWrapper(Map<Integer, String> columnSetIndexMap, Map<String, Object> columnNameValMap, BoundSql updateSql) {\n        if (columnSetIndexMap.size() <= columnNameValMap.size()) {\n            return;\n        }\n        MetaObject mpgenVal = SystemMetaObject.forObject(updateSql.getParameterObject());\n        if(!mpgenVal.hasGetter(Constants.WRAPPER)){\n            return;\n        }\n        Object ew = mpgenVal.getValue(Constants.WRAPPER);\n        if (ew instanceof UpdateWrapper || ew instanceof LambdaUpdateWrapper) {\n            final String sqlSet = ew instanceof UpdateWrapper ? ((UpdateWrapper) ew).getSqlSet() : ((LambdaUpdateWrapper) ew).getSqlSet();// columnName=#{val}\n            if (sqlSet == null) {\n                return;\n            }\n            MetaObject ewMeta = SystemMetaObject.forObject(ew);\n            final Map paramNameValuePairs = (Map) ewMeta.getValue(\"paramNameValuePairs\");\n            String[] setItems = sqlSet.split(\",\");\n            for (String setItem : setItems) {\n                //age=#{ew.paramNameValuePairs.MPGENVAL1}\n                String[] nameAndValuePair = setItem.split(\"=\", 2);\n                if (nameAndValuePair.length == 2) {\n                    String setColName = nameAndValuePair[0].trim().toUpperCase();\n                    String setColVal = nameAndValuePair[1].trim();//#{.mp}\n                    if (columnSetIndexMap.containsValue(setColName)) {\n                        String[] mpGenKeyArray = setColVal.split(\"\\\\.\");\n                        String mpGenKey = mpGenKeyArray[mpGenKeyArray.length - 1].replace(\"}\", \"\");\n                        final Object setVal = paramNameValuePairs.get(mpGenKey);\n                        if (setVal instanceof IEnum) {\n                            columnNameValMap.put(setColName, String.valueOf(((IEnum<?>) setVal).getValue()));\n                        } else {\n                            columnNameValMap.put(setColName, setVal);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private Map<String, String> detectInsertColumnValuesNonJdbcParameters(Insert insert) {\n        Map<String, String> columnNameValMap = new HashMap<>(4);\n        final Select select = insert.getSelect();\n        List<Column> columns = insert.getColumns();\n        if (select instanceof SetOperationList) {\n            SetOperationList setOperationList = (SetOperationList) select;\n            final List<Select> selects = setOperationList.getSelects();\n            if (CollectionUtils.isEmpty(selects)) {\n                return columnNameValMap;\n            }\n            final Select selectBody = selects.get(0);\n            if (!(selectBody instanceof Values)) {\n                return columnNameValMap;\n            }\n            Values valuesStatement = (Values) selectBody;\n            if (valuesStatement.getExpressions() instanceof ExpressionList) {\n                ExpressionList expressionList = valuesStatement.getExpressions();\n                List<Expression> expressions = expressionList;\n                for (Expression expression : expressions) {\n                    if (expression instanceof RowConstructor) {\n                        final ExpressionList exprList = ((RowConstructor) expression);\n                        final List<Expression> insertExpList = exprList;\n                        for (int i = 0; i < insertExpList.size(); ++i) {\n                            Expression e = insertExpList.get(i);\n                            if (!(e instanceof JdbcParameter)) {\n                                final String columnName = columns.get(i).getColumnName();\n                                final String val = e.toString();\n                                columnNameValMap.put(columnName, val);\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        return columnNameValMap;\n    }\n\n    private String getColumnNameByProperty(String propertyName, String tableName) {\n        for (TableInfo tableInfo : TableInfoHelper.getTableInfos()) {\n            if (tableName.equalsIgnoreCase(tableInfo.getTableName())) {\n                final List<TableFieldInfo> fieldList = tableInfo.getFieldList();\n                if (CollectionUtils.isEmpty(fieldList)) {\n                    return propertyName;\n                }\n                for (TableFieldInfo tableFieldInfo : fieldList) {\n                    if (propertyName.equalsIgnoreCase(tableFieldInfo.getProperty())) {\n                        return tableFieldInfo.getColumn().toUpperCase();\n                    }\n                }\n                return propertyName;\n            }\n        }\n        return propertyName;\n    }\n\n\n    private Map<String, Object> buildParameterObjectMap(BoundSql boundSql) {\n        MetaObject metaObject = PluginUtils.getMetaObject(boundSql.getParameterObject());\n        Map<String, Object> propertyValMap = new HashMap<>(boundSql.getParameterMappings().size());\n        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {\n            String propertyName = parameterMapping.getProperty();\n            if (propertyName.startsWith(\"ew.paramNameValuePairs\")) {\n                continue;\n            }\n            Object propertyValue = metaObject.getValue(propertyName);\n            propertyValMap.put(propertyName, propertyValue);\n        }\n        return propertyValMap;\n\n    }\n\n\n    private String buildOriginalData(Select selectStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            final ResultSetMetaData metaData = resultSet.getMetaData();\n            int columnCount = metaData.getColumnCount();\n            StringBuilder sb = new StringBuilder(\"[\");\n            int count = 0;\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch delete limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                sb.append(\"{\");\n                for (int i = 1; i <= columnCount; ++i) {\n                    sb.append(\"\\\"\").append(metaData.getColumnName(i)).append(\"\\\":\\\"\");\n                    Object res = resultSet.getObject(i);\n                    if (res instanceof Clob) {\n                        sb.append(DataColumnChangeResult.convertClob((Clob) res));\n                    } else {\n                        sb.append(res);\n                    }\n                    sb.append(\"\\\",\");\n                }\n                sb.replace(sb.length() - 1, sb.length(), \"}\");\n            }\n            sb.append(\"]\");\n            resultSet.close();\n            return sb.toString();\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe deleted for selectStmt={}\", selectStmt, e);\n            return \"failed to get original data\";\n        }\n    }\n\n    private OriginalDataObj buildOriginalObjectData(Map<String, Object> updatedColumnDatas, Select selectStmt, Column pk, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        try (PreparedStatement statement = connection.prepareStatement(selectStmt.toString())) {\n            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);\n            parameterHandler.setParameters(statement);\n            ResultSet resultSet = statement.executeQuery();\n            List<DataChangedRecord> originalObjectDatas = new LinkedList<>();\n            int count = 0;\n\n            while (resultSet.next()) {\n                ++count;\n                if (checkTableBatchLimitExceeded(selectStmt, count)) {\n                    logger.error(\"batch update limit exceed: count={}, BATCH_UPDATE_LIMIT={}\", count, BATCH_UPDATE_LIMIT);\n                    throw DataUpdateLimitationException.DEFAULT;\n                }\n                originalObjectDatas.add(prepareOriginalDataObj(updatedColumnDatas, resultSet, pk));\n            }\n            OriginalDataObj result = new OriginalDataObj();\n            result.setOriginalDataObj(originalObjectDatas);\n            resultSet.close();\n            return result;\n        } catch (Exception e) {\n            if (e instanceof DataUpdateLimitationException) {\n                throw (DataUpdateLimitationException) e;\n            }\n            logger.error(\"try to get record tobe updated for selectStmt={}\", selectStmt, e);\n            return new OriginalDataObj();\n        }\n    }\n\n    /**\n     * 防止出现全表批量更新\n     * 默认一次更新不超过1000条\n     *\n     * @param selectStmt\n     * @param count\n     * @return\n     */\n    private boolean checkTableBatchLimitExceeded(Select selectStmt, int count) {\n        if (!batchUpdateLimitationOpened) {\n            return false;\n        }\n        final PlainSelect selectBody = (PlainSelect) selectStmt;\n        final FromItem fromItem = selectBody.getFromItem();\n        if (fromItem instanceof Table) {\n            Table fromTable = (Table) fromItem;\n            final String tableName = fromTable.getName().toUpperCase();\n            if (!BATCH_UPDATE_LIMIT_MAP.containsKey(tableName)) {\n                if (count > BATCH_UPDATE_LIMIT) {\n                    logger.error(\"batch update limit exceed for tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                        tableName, BATCH_UPDATE_LIMIT, count);\n                    return true;\n                }\n                return false;\n            }\n            final Integer limit = BATCH_UPDATE_LIMIT_MAP.get(tableName);\n            if (count > limit) {\n                logger.error(\"batch update limit exceed for configured tableName={}, BATCH_UPDATE_LIMIT={}, count={}\",\n                    tableName, limit, count);\n                return true;\n            }\n            return false;\n        }\n        return count > BATCH_UPDATE_LIMIT;\n    }\n\n\n    /**\n     * get records : include related column with original data in DB\n     *\n     * @param resultSet\n     * @param pk\n     * @return\n     * @throws SQLException\n     */\n    private DataChangedRecord prepareOriginalDataObj(Map<String, Object> updatedColumnDatas, ResultSet resultSet, Column pk) throws SQLException {\n        final ResultSetMetaData metaData = resultSet.getMetaData();\n        int columnCount = metaData.getColumnCount();\n        List<DataColumnChangeResult> originalColumnDatas = new LinkedList<>();\n        DataColumnChangeResult pkval = null;\n        for (int i = 1; i <= columnCount; ++i) {\n            String columnName = metaData.getColumnName(i).toUpperCase();\n            DataColumnChangeResult col;\n            Object updateVal = updatedColumnDatas.get(columnName);\n            if (updateVal != null && updateVal.getClass().getCanonicalName().startsWith(\"java.\")) {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i, updateVal.getClass()));\n            } else {\n                col = DataColumnChangeResult.constrcutByOriginalVal(columnName, resultSet.getObject(i));\n            }\n            if (pk != null && columnName.equalsIgnoreCase(pk.getColumnName())) {\n                pkval = col;\n            } else {\n                originalColumnDatas.add(col);\n            }\n        }\n        DataChangedRecord changedRecord = new DataChangedRecord();\n        changedRecord.setOriginalColumnDatas(originalColumnDatas);\n        if (pkval != null) {\n            changedRecord.setPkColumnName(pkval.getColumnName());\n            changedRecord.setPkColumnVal(pkval.getOriginalValue());\n        }\n        return changedRecord;\n    }\n\n\n    private Columns2SelectItemsResult buildColumns2SelectItems(String tableName, List<Column> columns) {\n        if (columns == null || columns.isEmpty()) {\n            return Columns2SelectItemsResult.build(Collections.singletonList(new SelectItem<>(new AllColumns())), 0);\n        }\n        List<SelectItem<?>> selectItems = new ArrayList<>(columns.size());\n        for (Column column : columns) {\n            selectItems.add(new SelectItem<>(column));\n        }\n        TableInfo tableInfo = getTableInfoByTableName(tableName);\n        if (tableInfo == null || StringUtils.isBlank(tableInfo.getKeyColumn())) {\n            return Columns2SelectItemsResult.build(selectItems, 0);\n        }\n        Column pk = new Column(tableInfo.getKeyColumn());\n        selectItems.add(new SelectItem<>(pk));\n        Columns2SelectItemsResult result = Columns2SelectItemsResult.build(selectItems, 1);\n        result.setPk(pk);\n        return result;\n    }\n\n    private String buildParameterObject(BoundSql boundSql) {\n        Object paramObj = boundSql.getParameterObject();\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"{\");\n        if (paramObj instanceof Map) {\n            Map<String, Object> paramMap = (Map<String, Object>) paramObj;\n            int index = 1;\n            boolean hasParamIndex = false;\n            String key;\n            while (paramMap.containsKey((key = \"param\" + index))) {\n                Object paramIndex = paramMap.get(key);\n                sb.append(\"\\\"\").append(key).append(\"\\\"\").append(\":\").append(\"\\\"\").append(paramIndex).append(\"\\\"\").append(\",\");\n                hasParamIndex = true;\n                ++index;\n            }\n            if (hasParamIndex) {\n                sb.delete(sb.length() - 1, sb.length());\n                sb.append(\"}\");\n                return sb.toString();\n            }\n            for (Map.Entry<String, Object> ety : paramMap.entrySet()) {\n                sb.append(\"\\\"\").append(ety.getKey()).append(\"\\\"\").append(\":\").append(\"\\\"\").append(ety.getValue()).append(\"\\\"\").append(\",\");\n            }\n            sb.delete(sb.length() - 1, sb.length());\n            sb.append(\"}\");\n            return sb.toString();\n        }\n        sb.append(\"param:\").append(paramObj);\n        sb.append(\"}\");\n        return sb.toString();\n    }\n\n    public OperationResult processDelete(Delete deleteStmt, MappedStatement mappedStatement, BoundSql boundSql, Connection connection) {\n        Table table = deleteStmt.getTable();\n        Expression where = deleteStmt.getWhere();\n        PlainSelect selectBody = new PlainSelect();\n        selectBody.setFromItem(table);\n        selectBody.setSelectItems(Collections.singletonList(new SelectItem<>((new AllColumns()))));\n        selectBody.setWhere(where);\n        String originalData = buildOriginalData(selectBody, mappedStatement, boundSql, connection);\n        OperationResult result = new OperationResult();\n        result.setOperation(\"delete\");\n        result.setTableName(table.getName());\n        result.setRecordStatus(originalData.startsWith(\"[\"));\n        result.setChangedData(originalData);\n        return result;\n    }\n\n    /**\n     * 设置批量更新记录条数上限\n     *\n     * @param limit\n     * @return\n     */\n    public DataChangeRecorderInnerInterceptor setBatchUpdateLimit(int limit) {\n        this.BATCH_UPDATE_LIMIT = limit;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor openBatchUpdateLimitation() {\n        this.batchUpdateLimitationOpened = true;\n        return this;\n    }\n\n    public DataChangeRecorderInnerInterceptor configTableLimitation(String tableName, int limit) {\n        this.BATCH_UPDATE_LIMIT_MAP.put(tableName.toUpperCase(), limit);\n        return this;\n    }\n\n    /**\n     * ignoredColumns = TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2; TABLE3.*; *.COLUMN1,COLUMN2\n     * 多个表用分号分隔\n     * TABLE_NAME1.COLUMN1,COLUMN2 : 表示忽略这个表的这2个字段\n     * TABLE3.*: 表示忽略这张表的INSERT/UPDATE，delete暂时还保留\n     * *.COLUMN1,COLUMN2:表示所有表的这个2个字段名都忽略\n     *\n     * @param properties\n     */\n    @Override\n    public void setProperties(Properties properties) {\n\n        String ignoredTableColumns = properties.getProperty(\"ignoredTableColumns\");\n        if (ignoredTableColumns == null || ignoredTableColumns.trim().isEmpty()) {\n            return;\n        }\n        String[] array = ignoredTableColumns.split(\";\");\n        for (String table : array) {\n            int index = table.indexOf(\".\");\n            if (index == -1) {\n                logger.warn(\"invalid data={} for ignoredColumns, format should be TABLE_NAME1.COLUMN1,COLUMN2; TABLE2.COLUMN1,COLUMN2;\", table);\n                continue;\n            }\n            String tableName = table.substring(0, index).trim().toUpperCase();\n            String[] columnArray = table.substring(index + 1).split(\",\");\n            Set<String> columnSet = new HashSet<>(columnArray.length);\n            for (String column : columnArray) {\n                column = column.trim().toUpperCase();\n                if (column.isEmpty()) {\n                    continue;\n                }\n                columnSet.add(column);\n            }\n            if (\"*\".equals(tableName)) {\n                ignoreAllColumns.addAll(columnSet);\n            } else {\n                this.ignoredTableColumns.put(tableName, columnSet);\n            }\n        }\n    }\n\n    @Data\n    public static class OperationResult {\n\n        private String operation;\n        private boolean recordStatus;\n        private String tableName;\n        private String changedData;\n        /**\n         * cost for this plugin, ms\n         */\n        private long cost;\n\n        public void buildDataStr(List<DataChangedRecord> records) {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"[\");\n            for (DataChangedRecord r : records) {\n                sb.append(r.generateUpdatedDataStr()).append(\",\");\n            }\n            if (sb.length() == 1) {\n                sb.append(\"]\");\n                changedData = sb.toString();\n                return;\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"]\");\n            changedData = sb.toString();\n        }\n\n        @Override\n        public String toString() {\n            return \"{\" +\n                \"\\\"tableName\\\":\\\"\" + tableName + \"\\\",\" +\n                \"\\\"operation\\\":\\\"\" + operation + \"\\\",\" +\n                \"\\\"recordStatus\\\":\\\"\" + recordStatus + \"\\\",\" +\n                \"\\\"changedData\\\":\" + changedData + \",\" +\n                \"\\\"cost(ms)\\\":\" + cost + \"}\";\n        }\n    }\n\n    @Data\n    public static class Columns2SelectItemsResult {\n\n        private Column pk;\n        /**\n         * all column with additional columns: ID, etc.\n         */\n        private List<SelectItem<?>> selectItems;\n        /**\n         * newly added column count from meta data.\n         */\n        private int additionalItemCount;\n\n        public static Columns2SelectItemsResult build(List<SelectItem<?>> selectItems, int additionalItemCount) {\n            Columns2SelectItemsResult result = new Columns2SelectItemsResult();\n            result.setSelectItems(selectItems);\n            result.setAdditionalItemCount(additionalItemCount);\n            return result;\n        }\n    }\n\n    @Data\n    public static class OriginalDataObj {\n\n        private List<DataChangedRecord> originalDataObj;\n\n        public boolean isEmpty() {\n            return originalDataObj == null || originalDataObj.isEmpty();\n        }\n\n    }\n\n    @Data\n    public static class DataColumnChangeResult {\n\n        private String columnName;\n        private Object originalValue;\n        private Object updateValue;\n\n        @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n        public boolean isDataChanged(Object updateValue) {\n            if (Objects.equals(originalValue, updateValue)) {\n                return false;\n            }\n            if (originalValue instanceof Clob) {\n                String originalStr = convertClob((Clob) originalValue);\n                setOriginalValue(originalStr);\n                return !originalStr.equals(updateValue);\n            }\n            if (originalValue instanceof Comparable) {\n                Comparable original = (Comparable) originalValue;\n                if (original.getClass().isInstance(updateValue)) {\n                    Comparable update = (Comparable) updateValue;\n                    return original.compareTo(update) != 0;\n                }\n            }\n            return true;\n        }\n\n        public static String convertClob(Clob clobObj) {\n            try {\n                return clobObj.getSubString(0, (int) clobObj.length());\n            } catch (Exception e) {\n                try (Reader is = clobObj.getCharacterStream()) {\n                    char[] chars = new char[64];\n                    int readChars;\n                    StringBuilder sb = new StringBuilder();\n                    while ((readChars = is.read(chars)) != -1) {\n                        sb.append(chars, 0, readChars);\n                    }\n                    return sb.toString();\n                } catch (Exception e2) {\n                    //ignored\n                    return \"unknown clobObj\";\n                }\n            }\n        }\n\n        public static DataColumnChangeResult constrcutByUpdateVal(String columnName, Object updateValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setUpdateValue(updateValue);\n            return res;\n        }\n\n        public static DataColumnChangeResult constrcutByOriginalVal(String columnName, Object originalValue) {\n            DataColumnChangeResult res = new DataColumnChangeResult();\n            res.setColumnName(columnName);\n            res.setOriginalValue(originalValue);\n            return res;\n        }\n\n        public String generateDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"\\\"\").append(columnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(originalValue)).append(\"->\").append(convertDoubleQuotes(updateValue)).append(\"\\\"\").append(\",\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    @Data\n    public static class DataChangedRecord {\n\n        private String pkColumnName;\n        private Object pkColumnVal;\n        private List<DataColumnChangeResult> originalColumnDatas;\n        private List<DataColumnChangeResult> updatedColumns;\n\n        public boolean hasUpdate(Map<String, Object> columnNameValMap, Set<String> ignoredColumns, Set<String> ignoreAllColumns) {\n            if (originalColumnDatas == null) {\n                return true;\n            }\n            boolean hasUpdate = false;\n            updatedColumns = new ArrayList<>(originalColumnDatas.size());\n            for (DataColumnChangeResult originalColumn : originalColumnDatas) {\n                final String columnName = originalColumn.getColumnName().toUpperCase();\n                if (ignoredColumns != null && ignoredColumns.contains(columnName) || ignoreAllColumns.contains(columnName)) {\n                    continue;\n                }\n                Object updatedValue = columnNameValMap.get(columnName);\n                if (originalColumn.isDataChanged(updatedValue)) {\n                    hasUpdate = true;\n                    originalColumn.setUpdateValue(updatedValue);\n                    updatedColumns.add(originalColumn);\n                }\n            }\n            return hasUpdate;\n        }\n\n        public String generateUpdatedDataStr() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"{\");\n            if (pkColumnName != null) {\n                sb.append(\"\\\"\").append(pkColumnName).append(\"\\\"\").append(\":\").append(\"\\\"\").append(convertDoubleQuotes(pkColumnVal)).append(\"\\\"\").append(\",\");\n            }\n            for (DataColumnChangeResult update : updatedColumns) {\n                sb.append(update.generateDataStr());\n            }\n            sb.replace(sb.length() - 1, sb.length(), \"}\");\n            return sb.toString();\n        }\n\n        public String convertDoubleQuotes(Object obj) {\n            if (obj == null) {\n                return null;\n            }\n            return obj.toString().replace(\"\\\"\", \"\\\\\\\"\");\n        }\n    }\n\n    public static class DataUpdateLimitationException extends MybatisPlusException {\n\n        public DataUpdateLimitationException(String message) {\n            super(message);\n        }\n\n        public static DataUpdateLimitationException DEFAULT = new DataUpdateLimitationException(\"本次操作 因超过系统安全阈值 被拦截，如需继续，请联系管理员!\");\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DataPermissionInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport lombok.ToString;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.select.SetOperationList;\nimport net.sf.jsqlparser.statement.select.WithItem;\nimport net.sf.jsqlparser.statement.update.Update;\n\n/**\n * 数据权限处理器\n *\n * @author hubin\n * @since 3.5.2\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class DataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private DataPermissionHandler dataPermissionHandler;\n\n    @SuppressWarnings(\"RedundantThrows\")\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            // 参照 com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.processSelect 做的修改\n            final String whereSegment = (String) obj;\n            processSelectBody(select, whereSegment);\n            List<WithItem> withItemsList = select.getWithItemsList();\n            if (!CollectionUtils.isEmpty(withItemsList)) {\n                withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));\n            }\n        } else {\n            // 兼容原来的旧版 DataPermissionHandler 场景\n            if (select instanceof PlainSelect) {\n                this.setWhere((PlainSelect) select, (String) obj);\n            } else if (select instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) select;\n                List<Select> selectBodyList = setOperationList.getSelects();\n                selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));\n            }\n        }\n    }\n\n    /**\n     * 设置 where 条件\n     *\n     * @param plainSelect  查询对象\n     * @param whereSegment 查询条件片段\n     */\n    protected void setWhere(PlainSelect plainSelect, String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return;\n        }\n        // 兼容旧版的数据权限处理\n        final Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), whereSegment);\n        if (null != sqlSegment) {\n            plainSelect.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(update.getTable(), update.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            update.setWhere(sqlSegment);\n        }\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        final Expression sqlSegment = getUpdateOrDeleteExpression(delete.getTable(), delete.getWhere(), (String) obj);\n        if (null != sqlSegment) {\n            delete.setWhere(sqlSegment);\n        }\n    }\n\n    protected Expression getUpdateOrDeleteExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        if (dataPermissionHandler instanceof MultiDataPermissionHandler) {\n            return andExpression(table, where, whereSegment);\n        } else {\n            // 兼容旧版的数据权限处理\n            return dataPermissionHandler.getSqlSegment(where, whereSegment);\n        }\n    }\n\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (dataPermissionHandler == null) {\n            return null;\n        }\n        // 只有新版数据权限处理器才会执行到这里\n        final MultiDataPermissionHandler handler = (MultiDataPermissionHandler) dataPermissionHandler;\n        return handler.getSqlSegment(table, where, whereSegment);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.DynamicTableNameHandler;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;\nimport lombok.Setter;\nimport net.sf.jsqlparser.statement.Statement;\nimport net.sf.jsqlparser.statement.UnsupportedStatement;\n\n/**\n * 动态表处理器 (基于JsqlParser解析器)\n * <p>默认情况下,如果JsqlParser解析不了,则调用父类的解析进行处理</p>\n *\n * @author nieqiurong\n * @see DynamicTableNameHandler\n * @since 3.5.11\n */\n@Setter\npublic class DynamicTableNameJsqlParserInnerInterceptor extends DynamicTableNameInnerInterceptor {\n\n    /**\n     * 是否忽略解析异常\n     */\n    private boolean ignoreException = false;\n\n    /**\n     * 当JsqlParser无法解析语句时是否进行调用父类继续解析处理\n     *\n     * @see com.baomidou.mybatisplus.core.toolkit.TableNameParser\n     */\n    private boolean shouldFallback = true;\n\n\n    @Deprecated\n    public DynamicTableNameJsqlParserInnerInterceptor() {\n    }\n\n    public DynamicTableNameJsqlParserInnerInterceptor(TableNameHandler tableNameHandler) {\n        super(tableNameHandler);\n    }\n\n    @Override\n    protected String processTableName(String sql) {\n        boolean unsupported = false;\n        try {\n            Statement statement = JsqlParserGlobal.parse(sql);\n            statement.accept(new DynamicTableNameHandler(sql, super.getTableNameHandler()));\n            if (statement instanceof UnsupportedStatement) {\n                unsupported = true;\n                return super.processTableName(sql);\n            }\n            return statement.toString();\n        } catch (Exception exception) {\n            return handleFallback(unsupported, sql, exception);\n        }\n    }\n\n    private String handleFallback(boolean unsupported, String sql, Exception originalException) {\n        Exception exception = originalException;\n        if (!unsupported || shouldFallback) {\n            try {\n                return super.processTableName(sql);\n            } catch (Exception e) {\n                exception = e;\n            }\n        }\n        if (ignoreException) {\n            return sql;\n        }\n        throw new MybatisPlusException(\"Table name processing failed : \", exception);\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/IllegalSQLInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.EncryptUtils;\nimport com.baomidou.mybatisplus.core.toolkit.PluginUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport net.sf.jsqlparser.expression.BinaryExpression;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.Function;\nimport net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.expression.operators.relational.InExpression;\nimport net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.FromItem;\nimport net.sf.jsqlparser.statement.select.Join;\nimport net.sf.jsqlparser.statement.select.ParenthesedSelect;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 由于开发人员水平参差不齐，即使订了开发规范很多人也不遵守\n * <p>SQL是影响系统性能最重要的因素，所以拦截掉垃圾SQL语句</p>\n * <br>\n * <p>拦截SQL类型的场景</p>\n * <p>1.必须使用到索引，包含left join连接字段，符合索引最左原则</p>\n * <p>必须使用索引好处，</p>\n * <p>1.1 如果因为动态SQL，bug导致update的where条件没有带上，全表更新上万条数据</p>\n * <p>1.2 如果检查到使用了索引，SQL性能基本不会太差</p>\n * <br>\n * <p>2.SQL尽量单表执行，有查询left join的语句，必须在注释里面允许该SQL运行，否则会被拦截，有left join的语句，如果不能拆成单表执行的SQL，请leader商量在做</p>\n * <p>https://gaoxianglong.github.io/shark</p>\n * <p>SQL尽量单表执行的好处</p>\n * <p>2.1 查询条件简单、易于开理解和维护；</p>\n * <p>2.2 扩展性极强；（可为分库分表做准备）</p>\n * <p>2.3 缓存利用率高；</p>\n * <p>2.在字段上使用函数</p>\n * <br>\n * <p>3.where条件为空</p>\n * <p>4.where条件使用了 !=</p>\n * <p>5.where条件使用了 not 关键字</p>\n * <p>6.where条件使用了 or 关键字</p>\n * <p>7.where条件使用了 使用子查询</p>\n *\n * @author willenfoo\n * @deprecated 3.5.10 实用性不高,语法分析太差,计划移除\n * @since 3.4.0\n */\n@Deprecated\npublic class IllegalSQLInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {\n\n    /**\n     * 缓存验证结果，提高性能\n     */\n    private static final Set<String> cacheValidResult = new HashSet<>();\n    /**\n     * 缓存表的索引信息\n     */\n    private static final Map<String, List<IndexInfo>> indexInfoMap = new ConcurrentHashMap<>();\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpStatementHandler.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || InterceptorIgnoreHelper.willIgnoreIllegalSql(ms.getId())) {\n            return;\n        }\n        BoundSql boundSql = mpStatementHandler.boundSql();\n        String originalSql = boundSql.getSql();\n        logger.debug(\"检查SQL是否合规，SQL:\" + originalSql);\n        String md5Base64 = EncryptUtils.md5Base64(originalSql);\n        if (cacheValidResult.contains(md5Base64)) {\n            logger.debug(\"该SQL已验证，无需再次验证，，SQL:\" + originalSql);\n            return;\n        }\n        parserSingle(originalSql, connection);\n        //缓存验证结果\n        cacheValidResult.add(md5Base64);\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        if (select instanceof PlainSelect) {\n            PlainSelect plainSelect = (PlainSelect) select;\n            FromItem fromItem = ((PlainSelect) select).getFromItem();\n            while (fromItem instanceof ParenthesedSelect) {\n                ParenthesedSelect parenthesedSelect = (ParenthesedSelect) fromItem;\n                plainSelect = (PlainSelect) parenthesedSelect.getSelect();\n                fromItem = plainSelect.getFromItem();\n            }\n            Expression where = plainSelect.getWhere();\n            Assert.notNull(where, \"非法SQL，必须要有where条件\");\n            Table table = (Table) plainSelect.getFromItem();\n            List<Join> joins = plainSelect.getJoins();\n            validWhere(where, table, (Connection) obj);\n            validJoins(joins, table, (Connection) obj);\n        }\n    }\n\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        Expression where = update.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = update.getTable();\n        List<Join> joins = update.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        Expression where = delete.getWhere();\n        Assert.notNull(where, \"非法SQL，必须要有where条件\");\n        Table table = delete.getTable();\n        List<Join> joins = delete.getJoins();\n        validWhere(where, table, (Connection) obj);\n        validJoins(joins, table, (Connection) obj);\n    }\n\n    /**\n     * 验证expression对象是不是 or、not等等\n     *\n     * @param expression ignore\n     */\n    private void validExpression(Expression expression) {\n        while (expression instanceof ParenthesedExpressionList) {\n            ParenthesedExpressionList<Expression> parenthesis = (ParenthesedExpressionList) expression;\n            expression = parenthesis.get(0);\n        }\n        //where条件使用了 or 关键字\n        if (expression instanceof OrExpression) {\n            OrExpression orExpression = (OrExpression) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【or】关键字，错误or信息：\" + orExpression.toString());\n        } else if (expression instanceof NotEqualsTo) {\n            NotEqualsTo notEqualsTo = (NotEqualsTo) expression;\n            throw new MybatisPlusException(\"非法SQL，where条件中不能使用【!=】关键字，错误!=信息：\" + notEqualsTo.toString());\n        } else if (expression instanceof BinaryExpression) {\n            BinaryExpression binaryExpression = (BinaryExpression) expression;\n            // TODO 升级 jsqlparser 后待实现\n//            if (binaryExpression.isNot()) {\n//                throw new MybatisPlusException(\"非法SQL，where条件中不能使用【not】关键字，错误not信息：\" + binaryExpression.toString());\n//            }\n            if (binaryExpression.getLeftExpression() instanceof Function) {\n                Function function = (Function) binaryExpression.getLeftExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用数据库函数，错误函数信息：\" + function.toString());\n            }\n            if (binaryExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) binaryExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        } else if (expression instanceof InExpression) {\n            InExpression inExpression = (InExpression) expression;\n            if (inExpression.getRightExpression() instanceof Subtraction) {\n                Subtraction subSelect = (Subtraction) inExpression.getRightExpression();\n                throw new MybatisPlusException(\"非法SQL，where条件中不能使用子查询，错误子查询SQL信息：\" + subSelect.toString());\n            }\n        }\n\n    }\n\n    /**\n     * 如果SQL用了 left Join，验证是否有or、not等等，并且验证是否使用了索引\n     *\n     * @param joins      ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validJoins(List<Join> joins, Table table, Connection connection) {\n        //允许执行join，验证jion是否使用索引等等\n        if (joins != null) {\n            for (Join join : joins) {\n                Table rightTable = (Table) join.getRightItem();\n                Collection<Expression> onExpressions = join.getOnExpressions();\n                for (Expression expression : onExpressions) {\n                    validWhere(expression, table, rightTable, connection);\n                }\n            }\n        }\n    }\n\n    /**\n     * 检查是否使用索引\n     *\n     * @param table      ignore\n     * @param columnName ignore\n     * @param connection ignore\n     */\n    private void validUseIndex(Table table, String columnName, Connection connection) {\n        //是否使用索引\n        boolean useIndexFlag = false;\n        if (StringUtils.isNotBlank(columnName)) {\n            //表存在的索引\n            String dbName = table.getSchemaName();\n            String tableName = table.getName();\n            String catalogName = table.getCatalogName();\n            columnName = SqlParserUtils.removeWrapperSymbol(columnName);\n            List<IndexInfo> indexInfos = getIndexInfos(catalogName, dbName, tableName, connection);\n            for (IndexInfo indexInfo : indexInfos) {\n                if (indexInfo.getColumnName().equalsIgnoreCase(columnName)) {\n                    useIndexFlag = true;\n                    break;\n                }\n            }\n        }\n        if (!useIndexFlag) {\n            throw new MybatisPlusException(\"非法SQL，SQL未使用到索引, table:\" + table.getName() + \", columnName:\" + columnName);\n        }\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Connection connection) {\n        validWhere(expression, table, null, connection);\n    }\n\n    /**\n     * 验证where条件的字段，是否有not、or等等，并且where的第一个字段，必须使用索引\n     *\n     * @param expression ignore\n     * @param table      ignore\n     * @param joinTable  ignore\n     * @param connection ignore\n     */\n    private void validWhere(Expression expression, Table table, Table joinTable, Connection connection) {\n        validExpression(expression);\n        if (expression instanceof BinaryExpression) {\n            //获得左边表达式\n            Expression leftExpression = ((BinaryExpression) expression).getLeftExpression();\n            validExpression(leftExpression);\n\n            //如果左边表达式为Column对象，则直接获得列名\n            if (leftExpression instanceof Column) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                if (joinTable != null && rightExpression instanceof Column) {\n                    if (Objects.equals(((Column) rightExpression).getTable().getName(), table.getAlias().getName())) {\n                        validUseIndex(table, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(joinTable, ((Column) leftExpression).getColumnName(), connection);\n                    } else {\n                        validUseIndex(joinTable, ((Column) rightExpression).getColumnName(), connection);\n                        validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                    }\n                } else {\n                    //获得列名\n                    validUseIndex(table, ((Column) leftExpression).getColumnName(), connection);\n                }\n            }\n            //如果BinaryExpression，进行迭代\n            else if (leftExpression instanceof BinaryExpression) {\n                validWhere(leftExpression, table, joinTable, connection);\n            }\n\n            //获得右边表达式，并分解\n            if (joinTable != null) {\n                Expression rightExpression = ((BinaryExpression) expression).getRightExpression();\n                validExpression(rightExpression);\n            }\n        }\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key       缓存key\n     * @param dbName    数据库名\n     * @param tableName 表名\n     * @param conn      数据库连接\n     * @return 索引信息\n     * @see #getIndexInfos(String, String, String, String, Connection)\n     * @deprecated 3.5.11\n     */\n    @Deprecated\n    public List<IndexInfo> getIndexInfos(String key, String dbName, String tableName, Connection conn) {\n        return getIndexInfos(key, null, dbName, tableName, conn);\n    }\n\n    /**\n     * 得到表的索引信息\n     *\n     * @param key         缓存key\n     * @param catalogName catalogName\n     * @param dbName      数据库名\n     * @param tableName   表名\n     * @param conn        数据库连接\n     * @return 索引信息\n     * @since 3.5.11\n     */\n    public List<IndexInfo> getIndexInfos(String key, String catalogName, String dbName, String tableName, Connection conn) {\n        List<IndexInfo> indexInfos = null;\n        if (StringUtils.isNotBlank(key)) {\n            indexInfos = indexInfoMap.get(key);\n        }\n        if (indexInfos == null || indexInfos.isEmpty()) {\n            ResultSet rs;\n            try {\n                DatabaseMetaData metadata = conn.getMetaData();\n                String catalog = StringUtils.isBlank(catalogName) ? conn.getCatalog() : catalogName;\n                String schema = StringUtils.isBlank(dbName) ? conn.getSchema() : dbName;\n                rs = metadata.getIndexInfo(catalog, schema, SqlParserUtils.removeWrapperSymbol(tableName), false, true);\n                indexInfos = new ArrayList<>();\n                while (rs.next()) {\n                    //索引中的列序列号等于1，才有效\n                    if (Objects.equals(rs.getString(8), \"1\")) {\n                        IndexInfo indexInfo = new IndexInfo();\n                        indexInfo.setDbName(rs.getString(1));\n                        indexInfo.setTableName(rs.getString(3));\n                        indexInfo.setColumnName(rs.getString(9));\n                        indexInfos.add(indexInfo);\n                    }\n                }\n                if (StringUtils.isNotBlank(key)) {\n                    indexInfoMap.put(key, indexInfos);\n                }\n            } catch (SQLException e) {\n                logger.error(String.format(\"getIndexInfo fault, with key:%s, dbName:%s, tableName:%s\", key, dbName, tableName), e);\n            }\n        }\n        return indexInfos;\n    }\n\n    /**\n     * 索引对象\n     */\n    @Data\n    private static class IndexInfo {\n\n        private String dbName;\n\n        private String tableName;\n\n        private String columnName;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/PaginationInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;\nimport com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Alias;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.select.*;\nimport org.apache.ibatis.cache.CacheKey;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.ParameterMapping;\nimport org.apache.ibatis.mapping.ResultMap;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\n/**\n * 分页拦截器\n * <p>\n * 默认对 left join 进行优化,虽然能优化count,但是加上分页的话如果1对多本身结果条数就是不正确的\n *\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@SuppressWarnings({\"rawtypes\"})\npublic class PaginationInnerInterceptor implements InnerInterceptor {\n    /**\n     * 获取jsqlparser中count的SelectItem\n     */\n    protected static final List<SelectItem<?>> COUNT_SELECT_ITEM = Collections.singletonList(\n        new SelectItem<>(new Column().withColumnName(\"COUNT(*)\")).withAlias(new Alias(\"total\"))\n    );\n    protected static final Map<String, MappedStatement> countMsCache = new ConcurrentHashMap<>();\n    protected final Log logger = LogFactory.getLog(this.getClass());\n\n\n    /**\n     * 溢出总页数后是否进行处理\n     */\n    protected boolean overflow;\n    /**\n     * 单页分页条数限制\n     */\n    protected Long maxLimit;\n    /**\n     * 数据库类型\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private DbType dbType;\n    /**\n     * 方言实现类\n     * <p>\n     * 查看 {@link #findIDialect(Executor)} 逻辑\n     */\n    private IDialect dialect;\n    /**\n     * 生成 countSql 优化掉 join\n     * 现在只支持 left join\n     *\n     * @since 3.4.2\n     */\n    protected boolean optimizeJoin = true;\n\n    public PaginationInnerInterceptor(DbType dbType) {\n        this.dbType = dbType;\n    }\n\n    public PaginationInnerInterceptor(IDialect dialect) {\n        this.dialect = dialect;\n    }\n\n    /**\n     * 这里进行count,如果count为0这返回false(就是不再执行sql了)\n     */\n    @Override\n    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {\n            return true;\n        }\n\n        BoundSql countSql;\n        MappedStatement countMs = buildCountMappedStatement(ms, page.countId());\n        if (countMs != null) {\n            countSql = countMs.getBoundSql(parameter);\n        } else {\n            countMs = buildAutoCountMappedStatement(ms);\n            String countSqlStr = autoCountSql(page, boundSql.getSql());\n            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n            countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);\n            PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());\n        }\n\n        CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);\n        List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);\n        long total = 0;\n        if (CollectionUtils.isNotEmpty(result)) {\n            // 个别数据库 count 没数据不会返回 0\n            Object o = result.get(0);\n            if (o != null) {\n                total = Long.parseLong(o.toString());\n            }\n        }\n        page.setTotal(total);\n        return continuePage(page);\n    }\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);\n        if (null == page) {\n            return;\n        }\n\n        // 处理 orderBy 拼接\n        boolean addOrdered = false;\n        String buildSql = boundSql.getSql();\n        List<OrderItem> orders = page.orders();\n        if (CollectionUtils.isNotEmpty(orders)) {\n            addOrdered = true;\n            buildSql = this.concatOrderBy(buildSql, orders);\n        }\n\n        // size 小于 0 且不限制返回值则不构造分页sql\n        Long _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;\n        if (page.getSize() < 0 && null == _limit) {\n            if (addOrdered) {\n                PluginUtils.mpBoundSql(boundSql).sql(buildSql);\n            }\n            return;\n        }\n\n        handlerLimit(page, _limit);\n        IDialect dialect = findIDialect(executor);\n\n        final Configuration configuration = ms.getConfiguration();\n        DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());\n        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);\n\n        List<ParameterMapping> mappings = mpBoundSql.parameterMappings();\n        Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();\n        model.consumers(mappings, configuration, additionalParameter);\n        mpBoundSql.sql(model.getDialectSql());\n        mpBoundSql.parameterMappings(mappings);\n    }\n\n    /**\n     * 获取分页方言类的逻辑\n     *\n     * @param executor Executor\n     * @return 分页方言类\n     */\n    protected IDialect findIDialect(Executor executor) {\n        if (dialect != null) {\n            return dialect;\n        }\n        if (dbType != null) {\n            dialect = DialectFactory.getDialect(dbType);\n            return dialect;\n        }\n        return DialectFactory.getDialect(JdbcUtils.getDbType(executor));\n    }\n\n    /**\n     * 获取指定的 id 的 MappedStatement\n     *\n     * @param ms      MappedStatement\n     * @param countId id\n     * @return MappedStatement\n     */\n    protected MappedStatement buildCountMappedStatement(MappedStatement ms, String countId) {\n        if (StringUtils.isNotBlank(countId)) {\n            final String id = ms.getId();\n            if (!countId.contains(StringPool.DOT)) {\n                countId = id.substring(0, id.lastIndexOf(StringPool.DOT) + 1) + countId;\n            }\n            final Configuration configuration = ms.getConfiguration();\n            try {\n                return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> configuration.getMappedStatement(key, false));\n            } catch (Exception e) {\n                logger.warn(String.format(\"can not find this countId: [\\\"%s\\\"]\", countId));\n            }\n        }\n        return null;\n    }\n\n    /**\n     * 构建 mp 自用自动的 MappedStatement\n     *\n     * @param ms MappedStatement\n     * @return MappedStatement\n     */\n    protected MappedStatement buildAutoCountMappedStatement(MappedStatement ms) {\n        final String countId = ms.getId() + \"_mpCount\";\n        final Configuration configuration = ms.getConfiguration();\n        return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> {\n            MappedStatement.Builder builder = new MappedStatement.Builder(configuration, key, ms.getSqlSource(), ms.getSqlCommandType());\n            builder.resource(ms.getResource());\n            builder.fetchSize(ms.getFetchSize());\n            builder.statementType(ms.getStatementType());\n            builder.timeout(ms.getTimeout());\n            builder.parameterMap(ms.getParameterMap());\n            builder.resultMaps(Collections.singletonList(new ResultMap.Builder(configuration, Constants.MYBATIS_PLUS, Long.class, Collections.emptyList()).build()));\n            builder.resultSetType(ms.getResultSetType());\n            builder.cache(ms.getCache());\n            builder.flushCacheRequired(ms.isFlushCacheRequired());\n            builder.useCache(ms.isUseCache());\n            return builder.build();\n        });\n    }\n\n    /**\n     * 获取自动优化的 countSql\n     *\n     * @param page 参数\n     * @param sql  sql\n     * @return countSql\n     */\n    public String autoCountSql(IPage<?> page, String sql) {\n        if (!page.optimizeCountSql()) {\n            return lowLevelCountSql(sql);\n        }\n        try {\n            Select select = (Select) JsqlParserGlobal.parse(sql);\n            // https://github.com/baomidou/mybatis-plus/issues/3920  分页增加union语法支持\n            if (select instanceof SetOperationList) {\n                return lowLevelCountSql(sql);\n            }\n            PlainSelect plainSelect = (PlainSelect) select;\n\n            // 优化 order by 在非分组情况下\n            List<OrderByElement> orderBy = plainSelect.getOrderByElements();\n            if (CollectionUtils.isNotEmpty(orderBy)) {\n                boolean canClean = true;\n                for (OrderByElement order : orderBy) {\n                    // order by 里带参数,不去除order by\n                    Expression expression = order.getExpression();\n                    if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {\n                        canClean = false;\n                        break;\n                    }\n                }\n                if (canClean) {\n                    plainSelect.setOrderByElements(null);\n                }\n            }\n            Distinct distinct = plainSelect.getDistinct();\n            GroupByElement groupBy = plainSelect.getGroupBy();\n            // 包含 distinct、groupBy 不优化\n            if (null != distinct || null != groupBy) {\n                return lowLevelCountSql(select.toString());\n            }\n            //#95 Github, selectItems contains #{} ${}, which will be translated to ?, and it may be in a function: power(#{myInt},2)\n            for (SelectItem item : plainSelect.getSelectItems()) {\n                if (item.toString().contains(StringPool.QUESTION_MARK)) {\n                    return lowLevelCountSql(select.toString());\n                }\n            }\n\n            // 包含 join 连表,进行判断是否移除 join 连表\n            if (optimizeJoin && page.optimizeJoinOfCountSql()) {\n                List<Join> joins = plainSelect.getJoins();\n                if (CollectionUtils.isNotEmpty(joins)) {\n                    boolean canRemoveJoin = true;\n                    String whereS = Optional.ofNullable(plainSelect.getWhere()).map(Expression::toString).orElse(StringPool.EMPTY);\n                    // 不区分大小写\n                    whereS = whereS.toLowerCase();\n                    for (Join join : joins) {\n                        if (!join.isLeft()) {\n                            canRemoveJoin = false;\n                            break;\n                        }\n                        FromItem rightItem = join.getRightItem();\n                        String str = \"\";\n                        if (rightItem instanceof Table) {\n                            Table table = (Table) rightItem;\n                            str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;\n                        } else if (rightItem instanceof ParenthesedSelect) {\n                            ParenthesedSelect subSelect = (ParenthesedSelect) rightItem;\n                            /* 如果 left join 是子查询，并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {\n                                canRemoveJoin = false;\n                                break;\n                            }\n                            str = subSelect.getAlias().getName() + StringPool.DOT;\n                        }\n                        // 不区分大小写\n                        str = str.toLowerCase();\n\n                        if (whereS.contains(str)) {\n                            /* 如果 where 条件里包含使用 join 的表的字段作条件,就不移除 join */\n                            canRemoveJoin = false;\n                            break;\n                        }\n\n                        for (Expression expression : join.getOnExpressions()) {\n                            if (expression.toString().contains(StringPool.QUESTION_MARK)) {\n                                /* 如果 join 里包含 ?(代表有入参) 就不移除 join */\n                                canRemoveJoin = false;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (canRemoveJoin) {\n                        plainSelect.setJoins(null);\n                    }\n                }\n            }\n\n            // 优化 SQL\n            plainSelect.setSelectItems(COUNT_SELECT_ITEM);\n            return select.toString();\n        } catch (JSQLParserException e) {\n            // 无法优化使用原 SQL\n            logger.warn(\"optimize this sql to a count sql has exception, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"optimize this sql to a count sql has error, sql:\\\"\" + sql + \"\\\", exception:\\n\" + e);\n        }\n        return lowLevelCountSql(sql);\n    }\n\n    /**\n     * 无法进行count优化时,降级使用此方法\n     *\n     * @param originalSql 原始sql\n     * @return countSql\n     */\n    protected String lowLevelCountSql(String originalSql) {\n        return SqlParserUtils.getOriginalCountSql(originalSql);\n    }\n\n    /**\n     * 查询SQL拼接Order By\n     *\n     * @param originalSql 需要拼接的SQL\n     * @return ignore\n     */\n    public String concatOrderBy(String originalSql, List<OrderItem> orderList) {\n        try {\n            Select selectBody = (Select) JsqlParserGlobal.parse(originalSql);\n            if (selectBody instanceof PlainSelect) {\n                PlainSelect plainSelect = (PlainSelect) selectBody;\n                List<OrderByElement> orderByElements = plainSelect.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                plainSelect.setOrderByElements(orderByElementsReturn);\n                return plainSelect.toString();\n            } else if (selectBody instanceof SetOperationList) {\n                SetOperationList setOperationList = (SetOperationList) selectBody;\n                List<OrderByElement> orderByElements = setOperationList.getOrderByElements();\n                List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);\n                setOperationList.setOrderByElements(orderByElementsReturn);\n                return setOperationList.toString();\n            } else if (selectBody instanceof WithItem) {\n                // todo: don't known how to resole\n                return originalSql;\n            } else {\n                return originalSql;\n            }\n        } catch (JSQLParserException e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e.getCause());\n        } catch (Exception e) {\n            logger.warn(\"failed to concat orderBy from IPage, exception:\\n\" + e);\n        }\n        return originalSql;\n    }\n\n    protected List<OrderByElement> addOrderByElements(List<OrderItem> orderList, List<OrderByElement> orderByElements) {\n        List<OrderByElement> additionalOrderBy = orderList.stream()\n            .filter(item -> StringUtils.isNotBlank(item.getColumn()))\n            .map(item -> {\n                OrderByElement element = new OrderByElement();\n                element.setExpression(new Column(item.getColumn()));\n                element.setAsc(item.isAsc());\n                element.setAscDescPresent(true);\n                return element;\n            }).collect(Collectors.toList());\n        if (CollectionUtils.isEmpty(orderByElements)) {\n            return additionalOrderBy;\n        }\n        // github pull/3550 优化排序，比如：默认 order by id 前端传了name排序，设置为 order by name,id\n        additionalOrderBy.addAll(orderByElements);\n        return additionalOrderBy;\n    }\n\n    /**\n     * count 查询之后,是否继续执行分页\n     *\n     * @param page 分页对象\n     * @return 是否\n     */\n    protected boolean continuePage(IPage<?> page) {\n        if (page.getTotal() <= 0) {\n            return false;\n        }\n        if (page.getCurrent() > page.getPages()) {\n            if (overflow) {\n                //溢出总页数处理\n                handlerOverflow(page);\n            } else {\n                // 超过最大范围，未设置溢出逻辑中断 list 执行\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * 处理超出分页条数限制,默认归为限制数\n     *\n     * @param page IPage\n     */\n    protected void handlerLimit(IPage<?> page, Long limit) {\n        final long size = page.getSize();\n        if (limit != null && limit > 0 && (size > limit || size < 0)) {\n            page.setSize(limit);\n        }\n    }\n\n    /**\n     * 处理页数溢出,默认设置为第一页\n     *\n     * @param page IPage\n     */\n    protected void handlerOverflow(IPage<?> page) {\n        page.setCurrent(1);\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties)\n            .whenNotBlank(\"overflow\", Boolean::parseBoolean, this::setOverflow)\n            .whenNotBlank(\"dbType\", DbType::getDbType, this::setDbType)\n            .whenNotBlank(\"dialect\", ClassUtils::newInstance, this::setDialect)\n            .whenNotBlank(\"maxLimit\", Long::parseLong, this::setMaxLimit)\n            .whenNotBlank(\"optimizeJoin\", Boolean::parseBoolean, this::setOptimizeJoin);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/main/java/com/baomidou/mybatisplus/extension/plugins/inner/TenantLineInnerInterceptor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;\nimport com.baomidou.mybatisplus.core.toolkit.*;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;\nimport lombok.*;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.relational.EqualsTo;\nimport net.sf.jsqlparser.expression.operators.relational.ExpressionList;\nimport net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;\nimport net.sf.jsqlparser.schema.Column;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.select.*;\nimport net.sf.jsqlparser.statement.update.Update;\nimport net.sf.jsqlparser.statement.update.UpdateSet;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.executor.statement.StatementHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.session.ResultHandler;\nimport org.apache.ibatis.session.RowBounds;\n\nimport java.sql.Connection;\nimport java.util.List;\nimport java.util.Properties;\n\n/**\n * @author hubin\n * @since 3.4.0\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ToString(callSuper = true)\n@EqualsAndHashCode(callSuper = true)\n@SuppressWarnings({\"rawtypes\"})\npublic class TenantLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {\n\n    private TenantLineHandler tenantLineHandler;\n\n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n            return;\n        }\n        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);\n        mpBs.sql(parserSingle(mpBs.sql(), null));\n    }\n\n    @Override\n    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {\n        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);\n        MappedStatement ms = mpSh.mappedStatement();\n        SqlCommandType sct = ms.getSqlCommandType();\n        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {\n            if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {\n                return;\n            }\n            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();\n            mpBs.sql(parserMulti(mpBs.sql(), null));\n        }\n    }\n\n    @Override\n    protected void processSelect(Select select, int index, String sql, Object obj) {\n        final String whereSegment = (String) obj;\n        processSelectBody(select, whereSegment);\n        List<WithItem> withItemsList = select.getWithItemsList();\n        if (!CollectionUtils.isEmpty(withItemsList)) {\n            withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));\n        }\n    }\n\n    @Override\n    protected void processInsert(Insert insert, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(insert.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<Column> columns = insert.getColumns();\n        if (CollectionUtils.isEmpty(columns)) {\n            // 针对不给列名的insert 不处理\n            return;\n        }\n        String tenantIdColumn = tenantLineHandler.getTenantIdColumn();\n        if (tenantLineHandler.ignoreInsert(columns, tenantIdColumn)) {\n            // 针对已给出租户列的insert 不处理\n            return;\n        }\n        columns.add(new Column(tenantIdColumn));\n        Expression tenantId = tenantLineHandler.getTenantId();\n        // fixed gitee pulls/141 duplicate update\n        List<UpdateSet> duplicateUpdateColumns = insert.getDuplicateUpdateSets();\n        if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {\n            duplicateUpdateColumns.add(new UpdateSet(new Column(tenantIdColumn), tenantId));\n        }\n\n        Select select = insert.getSelect();\n        if (select instanceof PlainSelect) { //fix github issue 4998  修复升级到4.5版本的问题\n            this.processInsertSelect(select, (String) obj);\n        } else if (insert.getValues() != null) {\n            // fixed github pull/295\n            Values values = insert.getValues();\n            ExpressionList<Expression> expressions = (ExpressionList<Expression>) values.getExpressions();\n            if (expressions instanceof ParenthesedExpressionList) {\n                expressions.addExpression(tenantId);\n            } else {\n                if (CollectionUtils.isNotEmpty(expressions)) {//fix github issue 4998 jsqlparse 4.5 批量insert ItemsList不是MultiExpressionList 了，需要特殊处理\n                    int len = expressions.size();\n                    for (int i = 0; i < len; i++) {\n                        Expression expression = expressions.get(i);\n                        if (expression instanceof ParenthesedExpressionList) {\n                            ((ParenthesedExpressionList<Expression>) expression).addExpression(tenantId);\n                        } else {\n                            expressions.add(tenantId);\n                        }\n                    }\n                } else {\n                    expressions.add(tenantId);\n                }\n            }\n        } else {\n            throw ExceptionUtils.mpe(\"Failed to process multiple-table update, please exclude the tableName or statementId\");\n        }\n    }\n\n    /**\n     * update 语句处理\n     */\n    @Override\n    protected void processUpdate(Update update, int index, String sql, Object obj) {\n        final Table table = update.getTable();\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            // 过滤退出执行\n            return;\n        }\n        List<UpdateSet> sets = update.getUpdateSets();\n        if (!CollectionUtils.isEmpty(sets)) {\n            sets.forEach(us -> us.getValues().forEach(ex -> {\n                if (ex instanceof Select) {\n                    processSelectBody(((Select) ex), (String) obj);\n                }\n            }));\n        }\n        update.setWhere(this.andExpression(table, update.getWhere(), (String) obj));\n    }\n\n    /**\n     * delete 语句处理\n     */\n    @Override\n    protected void processDelete(Delete delete, int index, String sql, Object obj) {\n        if (tenantLineHandler.ignoreTable(delete.getTable().getName())) {\n            // 过滤退出执行\n            return;\n        }\n        delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere(), (String) obj));\n    }\n\n    /**\n     * 处理 insert into select\n     * <p>\n     * 进入这里表示需要 insert 的表启用了多租户,则 select 的表都启动了\n     *\n     * @param selectBody SelectBody\n     */\n    protected void processInsertSelect(Select selectBody, final String whereSegment) {\n        if(selectBody instanceof PlainSelect){\n            PlainSelect plainSelect = (PlainSelect) selectBody;\n            FromItem fromItem = plainSelect.getFromItem();\n            if (fromItem instanceof Table) {\n                // fixed gitee pulls/141 duplicate update\n                processPlainSelect(plainSelect, whereSegment);\n                appendSelectItem(plainSelect.getSelectItems());\n            } else if (fromItem instanceof Select) {\n                Select subSelect = (Select) fromItem;\n                appendSelectItem(plainSelect.getSelectItems());\n                processInsertSelect(subSelect, whereSegment);\n            }\n        } else if(selectBody instanceof ParenthesedSelect){\n            ParenthesedSelect parenthesedSelect = (ParenthesedSelect) selectBody;\n            processInsertSelect(parenthesedSelect.getSelect(), whereSegment);\n\n        }\n    }\n\n    /**\n     * 追加 SelectItem\n     *\n     * @param selectItems SelectItem\n     */\n    protected void appendSelectItem(List<SelectItem<?>> selectItems) {\n        if (CollectionUtils.isEmpty(selectItems)) {\n            return;\n        }\n        if (selectItems.size() == 1) {\n            SelectItem item = selectItems.get(0);\n            Expression expression = item.getExpression();\n            if (expression instanceof AllColumns) {\n                return;\n            }\n        }\n        selectItems.add(new SelectItem<>(new Column(tenantLineHandler.getTenantIdColumn())));\n    }\n\n    /**\n     * 租户字段别名设置\n     * <p>tenantId 或 tableAlias.tenantId</p>\n     *\n     * @param table 表对象\n     * @return 字段\n     */\n    protected Column getAliasColumn(Table table) {\n        StringBuilder column = new StringBuilder();\n        // todo 该起别名就要起别名,禁止修改此处逻辑\n        if (table.getAlias() != null) {\n            column.append(table.getAlias().getName()).append(StringPool.DOT);\n        }\n        column.append(tenantLineHandler.getTenantIdColumn());\n        return new Column(column.toString());\n    }\n\n    @Override\n    public void setProperties(Properties properties) {\n        PropertyMapper.newInstance(properties).whenNotBlank(\"tenantLineHandler\",\n            ClassUtils::newInstance, this::setTenantLineHandler);\n    }\n\n    /**\n     * 构建租户条件表达式\n     *\n     * @param table        表对象\n     * @param where        当前where条件\n     * @param whereSegment 所属Mapper对象全路径（在原租户拦截器功能中，这个参数并不需要参与相关判断）\n     * @return 租户条件表达式\n     * @see BaseMultiTableInnerInterceptor#buildTableExpression(Table, Expression, String)\n     */\n    @Override\n    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {\n        if (tenantLineHandler.ignoreTable(table.getName())) {\n            return null;\n        }\n        return new EqualsTo(getAliasColumn(table), tenantLineHandler.getTenantId());\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/JSqlParserTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.delete.Delete;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL 解析测试\n */\nclass JSqlParserTest {\n\n    @Test\n    void parser() throws Exception {\n        Select select = (Select) CCJSqlParserUtil.parse(\"SELECT a,b,c FROM tableName t WHERE t.col = 9 and b=c LIMIT 3, ?\");\n\n        PlainSelect ps = (PlainSelect) select;\n\n        System.out.println(ps.getWhere().toString());\n        System.out.println(ps.getSelectItems().get(1).toString());\n\n        AndExpression e = (AndExpression) ps.getWhere();\n        System.out.println(e.getLeftExpression());\n    }\n\n    @Test\n    void testDecr() throws JSQLParserException {\n        // 如果连一起 SqlParser 将无法解析 , 还有种处理方式就自减为负数的时候 转为 自增.\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a --110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a - -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a - -110\", parse2.toString());\n    }\n\n    @Test\n    void testIncr() throws JSQLParserException {\n        var parse1 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a +-110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse1.toString());\n        var parse2 = CCJSqlParserUtil.parse(\"UPDATE test SET a = a + -110\");\n        Assertions.assertEquals(\"UPDATE test SET a = a + -110\", parse2.toString());\n    }\n\n    @Test\n    void notLikeParser() throws Exception {\n        final String targetSql = \"SELECT * FROM tableName WHERE id NOT LIKE ?\";\n        Select select = (Select) CCJSqlParserUtil.parse(targetSql);\n        assertThat(select.toString()).isEqualTo(targetSql);\n    }\n\n    @Test\n    void updateWhereParser() throws Exception {\n        Update update = (Update) CCJSqlParserUtil.parse(\"Update tableName t SET t.a=(select c from tn where tn.id=t.id),b=2,c=3 \");\n        Assertions.assertNull(update.getWhere());\n    }\n\n    @Test\n    void deleteWhereParser() throws Exception {\n        Delete delete = (Delete) CCJSqlParserUtil.parse(\"delete from tableName t\");\n        Assertions.assertNull(delete.getWhere());\n    }\n\n    @Test\n    void testSelectForUpdate() throws Exception {\n        Assertions.assertEquals(\"SELECT * FROM t_demo WHERE a = 1 FOR UPDATE\",\n            CCJSqlParserUtil.parse(\"select * from t_demo where a = 1 for update\").toString());\n        Assertions.assertEquals(\"SELECT * FROM sys_sms_send_record WHERE check_status = 0 ORDER BY submit_time ASC LIMIT 10 FOR UPDATE\",\n            CCJSqlParserUtil.parse(\"select * from sys_sms_send_record where check_status = 0 for update order by submit_time asc limit 10\").toString());\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/parser/JsqlParserSimpleSerialTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser;\n\nimport com.baomidou.mybatisplus.extension.parser.cache.FstFactory;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.Statement;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.springframework.util.SerializationUtils;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2023-08-03\n */\nclass JsqlParserSimpleSerialTest {\n    private final static int len = 1000;\n    private final static String sql = \"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\";\n\n    @Test\n    @EnabledOnJre(JRE.JAVA_8)\n    void test() throws JSQLParserException {\n        System.out.println(\"循环次数: \" + len);\n        noSerial();\n        jdkSerial();\n        fstSerial();\n    }\n\n    void noSerial() throws JSQLParserException {\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            CCJSqlParserUtil.parse(sql);\n        }\n        long endTime = System.currentTimeMillis();\n        long e1 = endTime - startTime;\n        System.out.printf(\"普通解析执行耗时: %s 毫秒, 均耗时: %s%n\", e1, (double) e1 / len);\n    }\n\n    void jdkSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = SerializationUtils.serialize(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"jdk serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) SerializationUtils.deserialize(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"jdk deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n\n    void fstSerial() throws JSQLParserException {\n        Statement statement = CCJSqlParserUtil.parse(sql);\n        String target = statement.toString();\n        FstFactory factory = FstFactory.getDefaultFactory();\n        byte[] serial = null;\n        long startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            serial = factory.asByteArray(statement);\n        }\n        long endTime = System.currentTimeMillis();\n        long et = endTime - startTime;\n        System.out.printf(\"fst serialize 执行耗时: %s 毫秒,byte大小: %s, 均耗时: %s%n\", et, serial.length, (double) et / len);\n\n\n        startTime = System.currentTimeMillis();\n        for (int i = 0; i < len; i++) {\n            statement = (Statement) factory.asObject(serial);\n        }\n        endTime = System.currentTimeMillis();\n        et = endTime - startTime;\n        System.out.printf(\"fst deserialize 执行耗时: %s 毫秒, 均耗时: %s%n\", et, (double) et / len);\n        assertThat(statement).isNotNull();\n        assertThat(statement.toString()).isEqualTo(target);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/parser/cache/FstFactoryTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.parser.cache;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfo;\nimport io.github.classgraph.ScanResult;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author miemie\n * @since 2023-08-06\n */\nclass FstFactoryTest {\n\n    @Test\n    void clazz() {\n        List<ClassInfo> list = new ArrayList<>();\n        List<ClassInfo> absList = new ArrayList<>();\n        try (ScanResult scanResult = new ClassGraph().enableClassInfo().acceptPackages(\"net.sf.jsqlparser\").scan()) {\n            for (ClassInfo classInfo : scanResult.getAllClasses()) {\n                if (!classInfo.isInterface() && classInfo.implementsInterface(Serializable.class)) {\n                    if (classInfo.isAbstract()) {\n                        absList.add(classInfo);\n                        continue;\n                    }\n                    list.add(classInfo);\n                }\n            }\n        }\n        list.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n        System.out.println(\"↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓\");\n        absList.forEach(i -> System.out.printf(\"conf.registerClass(%s.class);%n\", i.getName().replace(\"$\", \".\")));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/MybatisPlusInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-30\n */\nclass MybatisPlusInterceptorTest {\n\n    @Test\n    void setProperties() {\n        Properties properties = new Properties();\n        properties.setProperty(\"@page\", \"com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor\");\n        properties.setProperty(\"page:maxLimit\", \"10\");\n        properties.setProperty(\"page:dbType\", \"h2\");\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.setProperties(properties);\n        List<InnerInterceptor> interceptors = interceptor.getInterceptors();\n\n        assertThat(interceptors).isNotEmpty();\n        assertThat(interceptors.size()).isEqualTo(1);\n\n        InnerInterceptor page = interceptors.getFirst();\n        assertThat(page).isInstanceOf(PaginationInnerInterceptor.class);\n\n        PaginationInnerInterceptor pii = (PaginationInnerInterceptor) page;\n        assertThat(pii.getMaxLimit()).isEqualTo(10);\n        assertThat(pii.getDbType()).isEqualTo(DbType.H2);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/BlockAttackInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-08-18\n */\nclass BlockAttackInnerInterceptorTest {\n\n    private final BlockAttackInnerInterceptor interceptor = new BlockAttackInnerInterceptor();\n\n    @Test\n    void update() {\n        checkEx(\"update user set name = null\", \"null where\");\n        checkEx(\"update user set name = null where 1=1\", \"1=1\");\n        checkEx(\"update user set name = null where 1<>2\", \"1<>2\");\n        checkEx(\"update user set name = null where 1!=2\", \"1!=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"update user set name = null where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n        checkEx(\"update user set name = null where 1=1 and (2=2)\", \"1=1 and (2=2)\");\n        checkEx(\"update user set name = null where (1=1 and 2=2)\", \"(1=1 and 2=2)\");\n        checkEx(\"update user set name = null where (1=1 and (2=3 or 3=3))\", \"(1=1 and (2=3 or 3=3))\");\n\n        checkNotEx(\"update user set name = null where 1=?\", \"1=?\");\n    }\n\n    @Test\n    void delete() {\n        checkEx(\"delete from user\", \"null where\");\n        checkEx(\"delete from user where 1=1\", \"1=1\");\n        checkEx(\"delete from user where 1<>2\", \"1<>2\");\n        checkEx(\"delete from user where 1!=2\", \"1!=2\");\n        checkEx(\"delete from user where 1=1 and 2=2\", \"1=1 and 2=2\");\n        checkEx(\"delete from user where 1=1 and 2=3 or 1=1\", \"1=1 and 2=3 or 1=1\");\n    }\n\n    void checkEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNotNull();\n        assertThat(e).as(as).isInstanceOf(MybatisPlusException.class);\n    }\n\n    void checkNotEx(String sql, String as) {\n        Exception e = null;\n        try {\n            interceptor.parserSingle(sql, null);\n        } catch (Exception x) {\n            e = x;\n        }\n        assertThat(e).as(as).isNull();\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataChangeRecorderInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataChangeRecorderInnerInterceptor;\nimport net.sf.jsqlparser.schema.Table;\nimport net.sf.jsqlparser.statement.insert.Insert;\nimport net.sf.jsqlparser.statement.update.Update;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Field;\nimport java.math.BigDecimal;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass DataChangeRecorderInnerInterceptorTest {\n\n    private final DataChangeRecorderInnerInterceptor interceptor = new DataChangeRecorderInnerInterceptor();\n\n    @BeforeEach\n    public void initProperties() {\n        Properties properties = new Properties();\n        properties.put(\"ignoredTableColumns\", \"table_name1.column1,column2; h2user.*; *.column1,COLUMN2\");\n        interceptor.setProperties(properties);\n    }\n    @Test\n    void setProperties() throws Exception {\n        final Object ignoreAllColumns = getFieldValue(interceptor, \"ignoreAllColumns\");\n        Assertions.assertEquals(Set.of(\"COLUMN1\", \"COLUMN2\"), ignoreAllColumns);\n        final Object ignoredTableColumns = getFieldValue(interceptor, \"ignoredTableColumns\");\n        Assertions.assertEquals(Map.of(\"H2USER\", Set.of(\"*\"), \"TABLE_NAME1\", Set.of(\"COLUMN1\", \"COLUMN2\")), ignoredTableColumns);\n    }\n\n    private Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {\n        final Field field = DataChangeRecorderInnerInterceptor.class.getDeclaredField(fieldName);\n        field.setAccessible(true);\n        return  field.get(obj);\n    }\n\n    @Test\n    void processInsert() {\n        final Insert insert = new Insert();\n        insert.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processInsert(insert, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void processUpdate() {\n        final Update update = new Update();\n        update.setTable(new Table(\"H2USER\"));\n        final DataChangeRecorderInnerInterceptor.OperationResult operationResult = interceptor.processUpdate(update, null, null, null);\n        Assertions.assertEquals(operationResult.getTableName(), \"H2USER:*\");\n        Assertions.assertFalse(operationResult.isRecordStatus());\n        Assertions.assertNull(operationResult.getChangedData());\n    }\n\n    @Test\n    void isDataChangedTest() {\n        var columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        Assertions.assertFalse(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new Object());\n        Assertions.assertTrue(columnChangeResult.isDataChanged(null));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(new BigDecimal(\"0\"));\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertFalse(columnChangeResult.isDataChanged(BigDecimal.ZERO));\n\n        columnChangeResult = new DataChangeRecorderInnerInterceptor.DataColumnChangeResult();\n        columnChangeResult.setOriginalValue(BigDecimal.ZERO);\n        Assertions.assertTrue(columnChangeResult.isDataChanged(\"0\"));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(0));\n\n        Assertions.assertFalse(columnChangeResult.isDataChanged(new BigDecimal(\"0\") {}));\n        Assertions.assertTrue(columnChangeResult.isDataChanged(new BigDecimal(\"1\") {}));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport lombok.extern.slf4j.Slf4j;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.operators.conditional.AndExpression;\nimport net.sf.jsqlparser.expression.operators.conditional.OrExpression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * 数据权限拦截器测试\n *\n * @author hubin\n * @since 3.4.1 +\n */\n@Slf4j\npublic class DataPermissionInterceptorTest {\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final Map<String, String> SQL_SEGMENT_MAP = new HashMap<>() {\n        {\n            put(TEST_1, \"username='123' or userId IN (1,2,3)\");\n            put(TEST_2, \"u.state=1 and u.amount > 1000\");\n            put(TEST_3, \"companyId in (1,2,3)\");\n            put(TEST_4, \"username like 'abc%'\");\n            put(TEST_5, \"id=1 and role_id in (select id from sys_role)\");\n        }\n    };\n\n    private static final DataPermissionInterceptor INTERCEPTOR = new DataPermissionInterceptor((where, mappedStatementId) -> {\n        try {\n            String sqlSegment = SQL_SEGMENT_MAP.get(mappedStatementId);\n            Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n            if (null != where) {\n                System.out.println(\"原 where : \" + where);\n                if (mappedStatementId.equals(TEST_4)) {\n                    // 这里测试返回 OR 条件\n                    return new OrExpression(where, sqlSegmentExpression);\n                }\n                return new AndExpression(where, sqlSegmentExpression);\n            }\n            return sqlSegmentExpression;\n        } catch (JSQLParserException e) {\n            log.error(\"解析错误:\", e);\n        }\n        return null;\n    });\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 OR username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(INTERCEPTOR.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * 动态表名内部拦截器测试\n *\n * @author miemie, hcl\n * @since 2020-07-16\n */\nclass DynamicTableNameInnerInterceptorTest {\n\n    /**\n     * 测试 SQL 中的动态表名替换\n     */\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void doIt() {\n        DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();\n        interceptor.setTableNameHandler((sql, tableName) -> tableName + \"_r\");\n\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM /**/t_user_r/* t_user */\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t left join entity_r e on e.id = t.id\", interceptor.changeTable(origin));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/DynamicTableNameJsqlParserInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameJsqlParserInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author nieqiurong\n */\nclass DynamicTableNameJsqlParserInnerInterceptorTest {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicTableNameJsqlParserInnerInterceptor.class);\n\n    private static final DynamicTableNameJsqlParserInnerInterceptor interceptor;\n\n    static {\n        interceptor = new DynamicTableNameJsqlParserInnerInterceptor((sql, tableName) -> {\n            LOGGER.info(\"process table : {}\", tableName);\n            if (tableName.endsWith(\"`\") || tableName.endsWith(\"]\")) {\n                char first = tableName.charAt(0);\n                char last = tableName.charAt(tableName.length()-1);\n                return first + SqlParserUtils.removeWrapperSymbol(tableName) + \"_r\" + last;\n            }\n            return tableName + \"_r\";\n        });\n        interceptor.setShouldFallback(true);\n    }\n\n\n\n    private static final String SQL_SELECT_SUB_QUERY = \"SELECT /*+ materialize*/ strategy_id\"\n        + \"FROM\"\n        + \" ( SELECT  strat.cf_strategy_id \"\n        + \"   FROM strategy strt,\"\n        + \"        doc_sect_ver prodGrp\"\n        + \"  WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"           AND strat.module_type   IN ('sdfdsf','assdf')\"\n        + \")\";\n\n\n    private static final String SQL_SELECT_THREE_JOIN_WITH_ALIASE = \"select c.name, s.name, s.id, r.result\"\n        + \" from colleges c \"\n        + \" join students s\"\n        + \"   on c.id = s.college_id\"\n        + \" join results r\"\n        + \"   on s.id = r.student_id\"\n        + \"where c.id = 3\"\n        + \"  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\";\n\n    private static final String SQL_COMPLEX_ONE = \"INSERT INTO static_product\"\n        + \"  (\"\n        + \"   DISCOUNT_ID,\"\n        + \"    CATEGORY_ID,\"\n        + \"    PRODUCT_ID\"\n        + \"   )\"\n        + \"  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,\"\n        + \"     ALLNDC11.PRODUCT_ID,\"\n        + \"     ALLNDC11.NDC11\"\n        + \"  FROM ITEM ITEM\"\n        + \" INNER JOIN\"\n        + \"   (SELECT NODE.SOURCE_ID NDC11,\"\n        + \"    PR.PRODUCT_ID,\"\n        + \"     BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"        DR_BD_PRODUCT PR,\"\n        + \"       map_edge_ver node\"\n        + \"     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"    AND B.BUNDLE_STATUS         =3\"\n        + \"    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"     AND NODE.EDGE_TYPE          = 1\"\n        + \"      START WITH\"\n        + \"      (\"\n        + \"        NODE.DEST_ID              = PR.PRODUCT_ID\"\n        + \"      AND B.BUNDLE_ID             =BD1.BUNDLE_ID\"\n        + \"      AND B.BUNDLE_STATUS         =3\"\n        + \"      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"     AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"      AND NODE.EDGE_TYPE          = 1\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"     )\"\n        + \"       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID\"\n        + \"    AND PRIOR NODE.EDGE_TYPE           = 1\"\n        + \"    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE\"\n        + \"   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE\"\n        + \"    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)\"\n        + \"    ) ALLNDC11\"\n        + \"  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)\"\n        + \"  UNION\"\n        + \"   ( SELECT BD1.BUNDLE_DISCOUNT_ID,\"\n        + \"      PR.PRODUCT_ID,\"\n        + \"     ITEM.CAT_MAP_ID\"\n        + \"    FROM DR_BUNDLE B,\"\n        + \"      DR_BUNDLE_DISCOUNT BD1,\"\n        + \"     DR_BD_PRODUCT PR,\"\n        + \"    ITEM ITEM\"\n        + \"    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID\"\n        + \"   AND B.BUNDLE_STATUS         =3\"\n        + \"   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID\"\n        + \"    AND BD1.IS_DYNAMIC_CATEGORY!= 1\"\n        + \"   AND item.cat_map_id         =pr.product_id\"\n        + \"    )\";\n\n    private static final String SQL_MERGE_COMPLEX = \"MERGE INTO  cf_procedure proc USING\"\n        + \" (\"\n        + \" WITH NON_STRATEGY_DETAILS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_strategy_id\"\n        + \"    FROM\"\n        + \"     ( SELECT  strat.cf_strategy_id\"\n        + \"        FROM cf_strategy strat,\"\n        + \"             struct_doc_Sect_ver prodGrp\"\n        + \"        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id\"\n        + \"                 AND strat.src_mgr_id     = prodGrp.mgr_id\"\n        + \"                 AND strat.src_ver_num    = prodGrp.ver_num\"\n        + \"                 AND strat.module_type   IN ('COMPL','PRCMSTR')\"\n        + \"   )  ),\"\n        + \"   NON_STRATEGY_COMPS AS\"\n        + \"   (\"\n        + \"   SELECT /*+ materialize*/ cf_component_id\"\n        + \"   FROM\"\n        + \"   (\"\n        + \"     SELECT comp.cf_component_id AS cf_component_id\"\n        + \"     FROM   cf_component comp,\"\n        + \"            tier_basis_ver tb\"\n        + \"     WHERE  comp.bucket_src_id   = tb.tier_basis_id\"\n        + \"             AND comp.bucket_src_mgr_id  = tb.mgr_id\"\n        + \"             AND comp.bucket_src_ver_num = tb.ver_num\"\n        + \"             AND comp.module_type       IN ('COMPL','PRCMSTR')\"\n        + \"   )\"\n        + \"   ) ,\"\n        + \" NON_STRAT_PERIODS AS (\"\n        + \"   SELECT /*+ materialize*/ cf_period_id\"\n        + \"   FROM\"\n        + \"         cf_period per,\"\n        + \"         struct_doc_sect_ver prodGrp\"\n        + \"   WHERE  per.src_id            = prodGrp.struct_doc_sect_id\"\n        + \"         AND per.src_mgr_id     = prodGrp.mgr_id\"\n        + \"         AND per.src_ver_num    = prodGrp.ver_num\"\n        + \"         AND per.module_type    IN ('COMPL','PRCMSTR')\"\n        + \"         AND per.pmt_status NOT IN ('TERM','REV')\"\n\n        + \"    SELECT DISTINCT cf_procedure_id\"\n        + \"   FROM\"\n        + \"     (SELECT /*+ LEADING(comp,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  non_strategy_comps comp,\"\n        + \"           cf_procedure proc\"\n        + \"     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND comp.cf_component_id    = proc.cf_component_id\"\n        + \"    UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"           proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM  cf_procedure proc,\"\n        + \"           non_strategy_details strat\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND strat.cf_strategy_id = proc.cf_strategy_id\"\n        + \"     UNION ALL\"\n        + \"     SELECT  /*+ LEADING(strat,proc)*/\"\n        + \"          proc.cf_procedure_id AS cf_procedure_id\"\n        + \"     FROM cf_procedure proc,\"\n        + \"          non_strat_periods periods\"\n        + \"     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'\"\n        + \"           AND periods.CF_PERIOD_ID = proc.period_id\"\n        + \"     )\"\n        + \"      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id)\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET proc.variable_name = 'TierResultSSName';\";\n\n    private static final String SQL_MERGE_COMPLEX_TWO = \" MERGE INTO cf_procedure_ver procVer USING\"\n        + \"   (SELECT cf_procedure_id\"\n        + \"    FROM cf_procedure proc\"\n        + \"    WHERE proc.variable_name                  = 'TierResultSSName'\"\n        + \"   ) proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id )\"\n        + \" WHEN MATCHED THEN\"\n        + \"   UPDATE SET procVer.variable_name = 'TierResultSSName'\"\n        + \"   WHERE procVer.variable_name <> 'TierResultSSName';\";\n\n    @Test\n    public void testSelectOneTable() {\n        var sql = \"SELECT name, age FROM table1 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTables() {\n        var sql = \"SELECT name, age FROM table1,table2\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTables() {\n        var sql = \"SELECT name, age FROM table1,table2,table3 group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r, table2_r, table3_r GROUP BY xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneTableWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectTwoTablesWithAliaseAndNoCondition() {\n        var sql = \"select xx from table1 a,table2 b\";\n        assertEquals(\"SELECT xx FROM table1_r a, table2_r b\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectThreeTablesWithAliase() {\n        var sql = \"SELECT name, age FROM table1 t1,table2 t2, table3 t3 whatever group by xyx\";\n        assertEquals(\"SELECT name, age FROM table1_r t1,table2_r t2, table3_r t3 whatever group by xyx\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectWithSubQuery() {\n        assertEquals(\"SELECT /*+ materialize */ strategy_idFROM(SELECT strat.cf_strategy_id FROM strategy_r strt, doc_sect_ver_r prodGrp WHERE strat.src_id = prodGrp.struct_doc_sect_id AND strat.module_type IN ('sdfdsf', 'assdf'))\", interceptor.changeTable(SQL_SELECT_SUB_QUERY));\n    }\n\n    @Test\n    public void testSelectWithOneJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneJoinWithAliase() {\n        var sql = \"SELECT coluname(s) FROM table1 t1 join table2 t2 ON t1.coluname=t2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r t1 JOIN table2_r t2 ON t1.coluname = t2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSelectOneLeftJoin() {\n        var sql = \"SELECT coluname(s) FROM table1 left outer join table2 ON table1.coluname=table2.coluname\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r LEFT OUTER JOIN table2_r ON table1.coluname = table2.coluname\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testShouldIgnoreDual() {\n        var sql = \"select * from dual\";\n        assertEquals(\"SELECT * FROM dual_r\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    public void testSelectTwoJoinWithAliase() {\n        assertEquals(\"select c.name, s.name, s.id, r.result from colleges_r c  join students_r s   on c.id = s.college_id join results_r r   on s.id = r.student_idwhere c.id = 3  and r.dt =  to_date('22-09-2005','dd-mm-yyyy')\", interceptor.changeTable(SQL_SELECT_THREE_JOIN_WITH_ALIASE));\n    }\n\n    @Test\n    public void testInsertWithValues() {\n        var sql = \"INSERT INTO table_name VALUES (value1,value2,value3,...)\";\n        assertEquals(\"INSERT INTO table_name_r VALUES (value1,value2,value3,...)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertComplex() {\n        assertEquals(\"INSERT INTO static_product_r  (   DISCOUNT_ID,    CATEGORY_ID,    PRODUCT_ID   )  ( SELECT DISTINCT ALLNDC11.BUNDLE_DISCOUNT_ID,     ALLNDC11.PRODUCT_ID,     ALLNDC11.NDC11  FROM ITEM_r ITEM INNER JOIN   (SELECT NODE.SOURCE_ID NDC11,    PR.PRODUCT_ID,     BD1.BUNDLE_DISCOUNT_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,        DR_BD_PRODUCT_r PR,       map_edge_ver_r node     WHERE B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE     AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE      AND B.BUNDLE_ID             =BD1.BUNDLE_ID    AND B.BUNDLE_STATUS         =3    AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1     AND NODE.EDGE_TYPE          = 1      START WITH      (        NODE.DEST_ID              = PR.PRODUCT_ID      AND B.BUNDLE_ID             =BD1.BUNDLE_ID      AND B.BUNDLE_STATUS         =3      AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID     AND BD1.IS_DYNAMIC_CATEGORY!= 1      AND NODE.EDGE_TYPE          = 1      AND B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE      AND B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE     )       CONNECT BY ( PRIOR NODE.SOURCE_ID=NODE.DEST_ID    AND PRIOR NODE.EDGE_TYPE           = 1    AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.EFF_START_DATE AND NODE.EFF_END_DATE   AND PRIOR B.DATE_ACTIVATED BETWEEN NODE.VER_START_DATE AND NODE.VER_END_DATE    AND prior bd1.bundle_discount_id= bd1.bundle_discount_id)    ) ALLNDC11  ON (ALLNDC11.NDC11 = ITEM.CAT_MAP_ID)  UNION   ( SELECT BD1.BUNDLE_DISCOUNT_ID,      PR.PRODUCT_ID,     ITEM.CAT_MAP_ID    FROM DR_BUNDLE_r B,      DR_BUNDLE_DISCOUNT_r BD1,     DR_BD_PRODUCT_r PR,    ITEM_r ITEM    WHERE B.BUNDLE_ID           =BD1.BUNDLE_ID   AND B.BUNDLE_STATUS         =3   AND PR.BUNDLE_DISCOUNT_ID   =BD1.BUNDLE_DISCOUNT_ID    AND BD1.IS_DYNAMIC_CATEGORY!= 1   AND item.cat_map_id         =pr.product_id    )\", interceptor.changeTable(SQL_COMPLEX_ONE));\n    }\n\n    @Test\n    public void testInsertWithSelect() {\n        var sql = \"INSERT INTO Customers (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers;\";\n        assertEquals(\"INSERT INTO Customers_r (CustomerName, Country) SELECT SupplierName, Country FROM Suppliers_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDelete2() {\n        var sql = \"DELETE FROM validation_task WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf WHERE conf_name IN ('SaleValidation'))\";\n        assertEquals(\"DELETE FROM validation_task_r WHERE task_name = 'ValidateSoldToCustId' AND conf_id IN (SELECT conf_id FROM validation_conf_r WHERE conf_name IN ('SaleValidation'))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOracleSpecialDelete() {\n        var sql = \"delete table1 where column_name=xyz\";\n        assertEquals(\"DELETE table1_r WHERE column_name = xyz\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter() {\n        var sql = \"ALTER TABLE Persons ADD UNIQUE (P_Id)\";\n        assertEquals(\"ALTER TABLE Persons_r ADD UNIQUE (P_Id)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testAlter2() {\n        var sql = \"ALTER TABLE table_name MODIFY coluname datatype\";\n        assertEquals(\"ALTER TABLE table_name_r MODIFY coluname datatype\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDrop() {\n        var sql = \"DROP table tname;\\n\\r\";\n        assertEquals(\"DROP table tname_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropFunction() {\n        var sql = \"DROP FUNCTION functionName;\";\n        assertEquals(\"DROP FUNCTION functionName\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropProcedure() {\n        var sql = \"drop procedure procedureName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropView() {\n        var sql = \"DROP view viewName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testDropIndex() {\n        var sql = \"DROP INDEX indexName\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUnionAll() {\n        var sql = \"SELECT coluname(s) FROM table1 UNION ALL SELECT coluname(s) FROM table2;\";\n        assertEquals(\"SELECT coluname(s) FROM table1_r UNION ALL SELECT coluname(s) FROM table2_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMerge() {\n        var sql = \"MERGE INTO employees e  USING hr_records h  ON (e.id = h.emp_id) WHEN MATCHED THEN  UPDATE SET e.address = h.address  WHEN NOT MATCHED THEN    INSERT (id, address) VALUES (h.emp_id, h.address);\";\n        assertEquals(\"MERGE INTO employees_r e USING hr_records_r h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeUsingQuery() {\n        var sql = \"MERGE INTO employees e USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h  ON (e.id = h.emp_id)  WHEN MATCHED THEN  UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\";\n        assertEquals(\"MERGE INTO employees_r e USING (SELECT * FROM hr_records_r WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h ON (e.id = h.emp_id) WHEN MATCHED THEN UPDATE SET e.address = h.address WHEN NOT MATCHED THEN INSERT (id, address) VALUES (h.emp_id, h.address)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testMergeComplexQuery() {\n        assertEquals(\"MERGE INTO  cf_procedure_r proc USING ( WITH NON_STRATEGY_DETAILS AS   (   SELECT /*+ materialize*/ cf_strategy_id    FROM     ( SELECT  strat.cf_strategy_id        FROM cf_strategy_r strat,             struct_doc_Sect_ver_r prodGrp        WHERE  strat.src_id               = prodGrp.struct_doc_sect_id                 AND strat.src_mgr_id     = prodGrp.mgr_id                 AND strat.src_ver_num    = prodGrp.ver_num                 AND strat.module_type   IN ('COMPL','PRCMSTR')   )  ),   NON_STRATEGY_COMPS AS   (   SELECT /*+ materialize*/ cf_component_id   FROM   (     SELECT comp.cf_component_id AS cf_component_id     FROM   cf_component_r comp,            tier_basis_ver_r tb     WHERE  comp.bucket_src_id   = tb.tier_basis_id             AND comp.bucket_src_mgr_id  = tb.mgr_id             AND comp.bucket_src_ver_num = tb.ver_num             AND comp.module_type       IN ('COMPL','PRCMSTR')   )   ) , NON_STRAT_PERIODS AS (   SELECT /*+ materialize*/ cf_period_id   FROM         cf_period_r per,         struct_doc_sect_ver_r prodGrp   WHERE  per.src_id            = prodGrp.struct_doc_sect_id         AND per.src_mgr_id     = prodGrp.mgr_id         AND per.src_ver_num    = prodGrp.ver_num         AND per.module_type    IN ('COMPL','PRCMSTR')         AND per.pmt_status NOT IN ('TERM','REV')    SELECT DISTINCT cf_procedure_id   FROM     (SELECT /*+ LEADING(comp,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  non_strategy_comps_r comp,           cf_procedure_r proc     WHERE proc.variable_name          ='CALCULATION_LEVEL_RESULT'           AND comp.cf_component_id    = proc.cf_component_id    UNION ALL     SELECT  /*+ LEADING(strat,proc)*/           proc.cf_procedure_id AS cf_procedure_id     FROM  cf_procedure_r proc,           non_strategy_details_r strat     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND strat.cf_strategy_id = proc.cf_strategy_id     UNION ALL     SELECT  /*+ LEADING(strat,proc)*/          proc.cf_procedure_id AS cf_procedure_id     FROM cf_procedure_r proc,          non_strat_periods_r periods     WHERE proc.variable_name       ='CALCULATION_LEVEL_RESULT'           AND periods.CF_PERIOD_ID = proc.period_id     )      )TMP ON (proc.cf_procedure_id = tmp.cf_procedure_id) WHEN MATCHED THEN   UPDATE SET proc.variable_name = 'TierResultSSName';\", interceptor.changeTable(SQL_MERGE_COMPLEX));\n    }\n\n    @Test\n    public void testMergeComplexQuery2() {\n        assertEquals(\"MERGE INTO cf_procedure_ver_r procVer USING (SELECT cf_procedure_id FROM cf_procedure_r proc WHERE proc.variable_name = 'TierResultSSName') proc_main ON (proc_main.cf_procedure_id = procVer.cf_procedure_id) WHEN MATCHED THEN UPDATE SET procVer.variable_name = 'TierResultSSName' WHERE procVer.variable_name <> 'TierResultSSName'\", interceptor.changeTable(SQL_MERGE_COMPLEX_TWO));\n    }\n\n    @Test\n    public void testCreateTable2() {\n        var sql = \"CREATE TABLE Persons(PersonID int,LastName varchar(255),FirstName varchar(255),Address varchar(255),City varchar(255));\";\n        assertEquals(\"CREATE TABLE Persons_r (PersonID int, LastName varchar (255), FirstName varchar (255), Address varchar (255), City varchar (255))\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateGlobalTable() {\n        var sql = \"CREATE GLOBAL TEMPORARY TABLE excl_cust (gen_name VARCHAR2(100),run_date TIMESTAMP(3), item_root_uuid  VARCHAR2(22), owner_member_id  NUMBER(20)) ON COMMIT DELETE ROWS\";\n        assertEquals(\"CREATE GLOBAL TEMPORARY TABLE excl_cust_r (gen_name VARCHAR2 (100), run_date TIMESTAMP (3), item_root_uuid VARCHAR2 (22), owner_member_id NUMBER (20)) ON COMMIT DELETE ROWS\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateIndex() {\n        var sql = \"CREATE INDEX temp_name_idx ON table1(name) NOLOGGING PARALLEL (DEGREE 8);\";\n        assertEquals(\"CREATE INDEX temp_name_idx ON table1_r (name) NOLOGGING PARALLEL (DEGREE8)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView() {\n        var sql = \"CREATE VIEW dept AS SELECT * FROM dept;\";\n        assertEquals(\"CREATE VIEW dept AS SELECT * FROM dept_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateView2() {\n        var sql = \"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp, dept WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno;\";\n        assertEquals(\"CREATE VIEW division1_staff AS SELECT ename, empno, job, dname FROM emp_r, dept_r WHERE emp.deptno IN (10, 30) AND emp.deptno = dept.deptno\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testCreateType() {\n        var sql = \"CREATE OR REPLACE TYPE TYPE_NAME IS TABLE OF VARCHAR2(100)\";\n        assertEquals(sql, interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTable() {\n        var sql = \"UPDATE tableName SET column1 = expression1, column2 = expression2\";\n        assertEquals(\"UPDATE tableName_r SET column1 = expression1, column2 = expression2\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery() {\n        var sql = \"UPDATE table1 SET table1.value = (SELECT table2.CODE FROM table2 WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE='blah' AND EXISTS (SELECT table2.CODE  FROM table2    WHERE table1.value = table2.DESC);\";\n        assertEquals(\"UPDATE table1_r SET table1.value = (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC) WHERE table1.UPDATETYPE = 'blah' AND EXISTS (SELECT table2.CODE FROM table2_r WHERE table1.value = table2.DESC)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQuery2() {\n        var sql = \"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1 INNER JOIN table2 ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\";\n        assertEquals(\"UPDATE (SELECT table1.value as OLD, table2.CODE as NEW FROM table1_r INNER JOIN table2_r ON table1.value = table2.DESC  WHERE table1.UPDATETYPE='blah' ) t SET t.OLD = t.NEW\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateTableSubQueryWithOracleHint() {\n        var sql = \"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\";\n        assertEquals(\"update /*+ PARALLEL OPT_PARAM('parallel_min_percent','0') */ eligible_r ec set ec.END_DATE = ec.END_DATE + INTERVAL '0 0:0:0.999' DAY TO SECOND\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testTruncateTable() {\n        var sql = \"truncate table eligible_item\";\n        assertEquals(\"TRUNCATE TABLE eligible_item_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithComment() {\n        var sql = \"select * from foo -- this is a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentContainingKeyword() {\n        var sql = \"select * from foo -- what happens if I say update in a comment\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentEndingWithKeyword() {\n        var sql = \"select * from foo -- what happens if I end a comment with an update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithCommentInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddle() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsAndNewlines() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n\\n join bar b -- I also like bar \\n on f.id = b.id\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testSqlWithMultipleCommentsInTheMiddleAndEnd() {\n        var sql = \"select * -- I like stars \\n from foo f -- I like foo \\n join bar b -- I also like bar \\n on f.id = b.id -- comment ending with update\";\n        assertEquals(\"SELECT * FROM foo_r f JOIN bar_r b ON f.id = b.id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectForUpdate() {\n        //TODO 暂时解决不能使用的问题,当碰到for update nowait这样的,后面的 nowait 会被当做成表但也不是很影响苗老板的动态表过滤.\n        var sql = \"select * from mp where id = 1 for update\";\n        assertEquals(\"SELECT * FROM mp_r WHERE id = 1 FOR UPDATE\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testOnDuplicateKeyUpdate () {\n        var sql = \"INSERT INTO cf_procedure (_id,password) VALUES ('1','password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword';\";\n        assertEquals(\"INSERT INTO cf_procedure_r (_id, password) VALUES ('1', 'password') ON DUPLICATE KEY UPDATE id = 'UpId', password = 'upPassword'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testUpdateIgnore() {\n        var sql = \"update ignore student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n\n        sql = \"UPDATE IGNORE student set name = 'abc' where id = 4\";\n        assertEquals(\"UPDATE IGNORE student_r SET name = 'abc' WHERE id = 4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    public void testInsertIgnore() {\n        var sql = \"INSERT IGNORE INTO student (userid,username) VALUES (2,'swan'),(4,'bear') ;\";\n        assertEquals(\"INSERT IGNORE INTO student_r (userid, username) VALUES (2, 'swan'), (4, 'bear')\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateUniqueIndex() {\n        var sql = \"CREATE UNIQUE INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE UNIQUE INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1_r ADD UNIQUE INDEX `a` (`a`)\";\n        assertEquals(\"ALTER TABLE table1_r_r ADD UNIQUE INDEX `a` (`a`)\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateFullTextIndex(){\n        var sql = \"CREATE FULLTEXT INDEX index_name ON table1 (a, b)\";\n        assertEquals(\"CREATE FULLTEXT INDEX index_name ON table1_r (a, b)\", interceptor.changeTable(sql));\n        sql = \"ALTER TABLE table1 ADD FULLTEXT INDEX `a`(`a`)\";\n        assertEquals(\"ALTER TABLE table1_r ADD FULLTEXT INDEX `a`(`a`)\", interceptor.changeTable(sql));\n    }\n\n\n    @Test\n    @SuppressWarnings({\"SqlDialectInspection\", \"SqlNoDataSourceInspection\"})\n    void test() {\n        // 表名相互包含\n        String origin = \"SELECT * FROM t_user, t_user_role\";\n        assertEquals(\"SELECT * FROM t_user_r, t_user_role_r\", interceptor.changeTable(origin));\n\n        // 表名在末尾\n        origin = \"SELECT * FROM t_user\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 表名前后有注释\n        origin = \"SELECT * FROM /**/t_user/* t_user */\";\n        assertEquals(\"SELECT * FROM t_user_r\", interceptor.changeTable(origin));\n\n        // 值中带有表名\n        origin = \"SELECT * FROM t_user WHERE name = 't_user'\";\n        assertEquals(\"SELECT * FROM t_user_r WHERE name = 't_user'\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t_user.* FROM t_user_real t_user\";\n        assertEquals(\"SELECT t_user.* FROM t_user_real_r t_user\", interceptor.changeTable(origin));\n\n        // 别名被声明要替换\n        origin = \"SELECT t.* FROM t_user_real t left join entity e on e.id = t.id\";\n        assertEquals(\"SELECT t.* FROM t_user_real_r t LEFT JOIN entity_r e ON e.id = t.id\", interceptor.changeTable(origin));\n    }\n\n    @Test\n    void testCreateTable() {\n        var sql = \"\"\"\n            CREATE TABLE `tag`  (\n              `id` int(11) NOT NULL AUTO_INCREMENT,\n              `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字',\n              `type` int(11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别',\n              PRIMARY KEY (`id`) USING BTREE\n            ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic;\n            \"\"\";\n        assertEquals(\"CREATE TABLE `tag_r` (`id` int (11) NOT NULL AUTO_INCREMENT, `name` varchar (50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签名字', `type` int (11) NULL DEFAULT NULL COMMENT '所属类别：0文章，1类别', PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '标签' ROW_FORMAT = Dynamic\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testCreateTableIfNotExists() {\n        var sql = \"\"\"\n            CREATE TABLE IF NOT EXISTS `user_info` (\n                `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,\n                `username` VARCHAR(50) NOT NULL UNIQUE,\n                `email` VARCHAR(100) NOT NULL UNIQUE,\n                `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n            \"\"\";\n        assertEquals(\"CREATE TABLE IF NOT EXISTS `user_info_r` (`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR (50) NOT NULL UNIQUE, `email` VARCHAR (100) NOT NULL UNIQUE, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDropTableIfExists() {\n        var sql = \"DROP TABLE IF EXISTS `tag`\";\n        assertEquals(\"DROP TABLE IF EXISTS `tag_r`\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testIssues6730() {\n        // https://github.com/baomidou/mybatis-plus/issues/6730\n        var sql = \"select * from user order by top_bottom_sort desc, 0- EXTRACT(EPOCH FROM req_delivery_time) desc\";\n        assertEquals(\"SELECT * FROM user_r ORDER BY top_bottom_sort DESC, 0 - EXTRACT(EPOCH FROM req_delivery_time) DESC\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectJoin() {\n        var sql = \"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\";\n        assertEquals(\"SELECT * FROM entity_r e JOIN entity1_r e1 ON e1.id = e.id WHERE e.id = ? OR e.name = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testSelectWithAs() {\n        var sql = \"with with_as_A as (select * from entity) select * from with_as_A\";\n        assertEquals(\"WITH with_as_A AS (SELECT * FROM entity_r) SELECT * FROM with_as_A_r\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDuplicateKeyUpdate() {\n        var sql = \"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\";\n        assertEquals(\"INSERT INTO entity_r (name, age) VALUES ('秋秋', 18), ('秋秋', '22') ON DUPLICATE KEY UPDATE age = 18\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testDelete() {\n        var sql = \"delete from entity where id = ?\";\n        assertEquals(\"DELETE FROM entity_r WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testUpdate() {\n        var sql = \"update entity set name = ? where id = ?\";\n        assertEquals(\"UPDATE entity_r SET name = ? WHERE id = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void testPartition() {\n        // 这种jsql解析不了\n        var sql = \"\"\"\n            -- 查询2023年Q2分区数据\n            SELECT\\s\n                region,\n                SUM(gross_profit) AS 区域总利润,\n                AVG(order_value) AS 平均订单金额\n            FROM\\s\n                sales_data\n            PARTITION BY\\s\n                (TO_DATE(order_date, 'YYYY-MM-DD'))\n            INTERVAL MONTHLY\n            FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\n            GROUP BY\\s\n                region;\n            \"\"\";\n        assertEquals(\"-- 查询2023年Q2分区数据\\n\" +\n            \"SELECT \\n\" +\n            \"    region,\\n\" +\n            \"    SUM(gross_profit) AS 区域总利润,\\n\" +\n            \"    AVG(order_value) AS 平均订单金额\\n\" +\n            \"FROM \\n\" +\n            \"    sales_data_r\\n\" +\n            \"PARTITION BY \\n\" +\n            \"    (TO_DATE(order_date, 'YYYY-MM-DD'))\\n\" +\n            \"INTERVAL MONTHLY\\n\" +\n            \"FOR PARTITION BETWEEN '2023-04-01' AND '2023-06-30'\\n\" +\n            \"GROUP BY \\n\" +\n            \"    region;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test2() {\n        var sql = \"\"\"\n            SELECT\\s\n                COUNT(*) AS 订单总数,\n                SUM(o.order_total) AS 总销售额,\n                SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数\n            FROM\\s\n                orders o\n            JOIN\\s\n                customers c ON o.customer_id = c.customer_id\n            JOIN\\s\n                order_items oi ON o.order_id = oi.order_id\n            WHERE\\s\n                c.region = 'North America'\n                AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30'\n            GROUP BY\\s\n                o.customer_id\n            HAVING\\s\n                COUNT(*) > 10;\n            ORDER BY\\s\n                total_sales DESC;\n            \"\"\";\n        assertEquals(\"SELECT COUNT(*) AS 订单总数, SUM(o.order_total) AS 总销售额, SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS 完成订单数 FROM orders_r o JOIN customers_r c ON o.customer_id = c.customer_id JOIN order_items_r oi ON o.order_id = oi.order_id WHERE c.region = 'North America' AND o.order_date BETWEEN '2023-04-01' AND '2023-04-30' GROUP BY o.customer_id HAVING COUNT(*) > 10\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test3() {\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            DELIMITER $$\n            DECLARE\\s\n                cur CURSOR FOR\\s\n                    SELECT employee_id FROM employees WHERE salary < 50000;\n                emp_id INT;\n            BEGIN\n                OPEN cur;\n                WHILE TRUE DO\n                    FETCH cur INTO emp_id;\n                    IF cur_rowcount = 0 THEN\n                        LEAVE;\n                    END IF;\n                   \\s\n                    UPDATE employees\\s\n                    SET salary = salary * 1.1\\s\n                    WHERE employee_id = emp_id;\n                   \\s\n                    INSERT INTO audit_log (employee_id, old_salary, new_salary)\n                    VALUES (emp_id, salary_before_update, salary_after_update);\n                END WHILE;\n                CLOSE cur;\n            END\n            $$\n            DELIMITER ;\n            \"\"\";\n        assertEquals(\"DELIMITER $$\\n\" +\n            \"DECLARE \\n\" +\n            \"    cur CURSOR FOR \\n\" +\n            \"        SELECT employee_id FROM employees_r WHERE salary < 50000;\\n\" +\n            \"    emp_id INT;\\n\" +\n            \"BEGIN\\n\" +\n            \"    OPEN cur;\\n\" +\n            \"    WHILE TRUE DO\\n\" +\n            \"        FETCH cur INTO emp_id_r;\\n\" +\n            \"        IF cur_rowcount = 0 THEN\\n\" +\n            \"            LEAVE;\\n\" +\n            \"        END IF;\\n\" +\n            \"        \\n\" +\n            \"        UPDATE employees_r \\n\" +\n            \"        SET salary = salary * 1.1 \\n\" +\n            \"        WHERE employee_id = emp_id;\\n\" +\n            \"        \\n\" +\n            \"        INSERT INTO audit_log_r (employee_id, old_salary, new_salary)\\n\" +\n            \"        VALUES (emp_id, salary_before_update, salary_after_update);\\n\" +\n            \"    END WHILE;\\n\" +\n            \"    CLOSE cur;\\n\" +\n            \"END\\n\" +\n            \"$$\\n\" +\n            \"DELIMITER ;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test4() {\n        var sql = \"\"\"\n            SELECT *\n            FROM employees e\n            JOIN departments d ON e.department_id = d.department_id\n            WHERE\\s\n                e.last_name LIKE CONCAT('%', :lastName, '%')\n                AND (\n                    d.department_name IN (:departmentList)\n                    OR :departmentList IS NULL\n                )\n                AND (\n                    e.hire_date >= :startDate\n                    OR :startDate IS NULL\n                )\n            ORDER BY\\s\n                e.employee_id\n            \"\"\";\n        assertEquals(\"SELECT * FROM employees_r e JOIN departments_r d ON e.department_id = d.department_id WHERE e.last_name LIKE CONCAT('%', :lastName, '%') AND (d.department_name IN (:departmentList) OR :departmentList IS NULL) AND (e.hire_date >= :startDate OR :startDate IS NULL) ORDER BY e.employee_id\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test5() {\n        var sql  = \"\"\"\n            SELECT\\s\n                product_id,\n                product_name,\n                stock_quantity,\n                (SELECT\\s\n                    SUM(ordered_qty)\\s\n                 FROM\\s\n                    purchase_orders po\\s\n                 WHERE\\s\n                    po.product_id = products.product_id\\s\n                    AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume\n            FROM\\s\n                products\n            WHERE\\s\n                stock_quantity < (\n                    SELECT\\s\n                        AVG(recommended_stock)\\s\n                    FROM\\s\n                        product_settings\\s\n                    WHERE\\s\n                        product_id = products.product_id\n                )\n                AND recent_order_volume > 500\n            \"\"\";\n        assertEquals(\"SELECT product_id, product_name, stock_quantity, (SELECT SUM(ordered_qty) FROM purchase_orders_r po WHERE po.product_id = products.product_id AND po.order_date >= CURDATE() - INTERVAL 3 MONTH) AS recent_order_volume FROM products_r WHERE stock_quantity < (SELECT AVG(recommended_stock) FROM product_settings_r WHERE product_id = products.product_id) AND recent_order_volume > 500\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test6() {\n        var sql = \"\"\"\n            WITH user_activity AS (\n                SELECT\\s\n                    user_id,\n                    event_type,\n                    event_time,\n                    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq\n                FROM\\s\n                    user_events\n            )\n            SELECT\\s\n                user_id,\n                event_type,\n                event_time,\n                LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time\n            FROM\\s\n                user_activity\n            WHERE\\s\n                activity_seq = 5\n            \"\"\";\n        assertEquals(\"WITH user_activity AS (SELECT user_id, event_type, event_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_time) AS activity_seq FROM user_events_r) SELECT user_id, event_type, event_time, LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_event_time FROM user_activity_r WHERE activity_seq = 5\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test7() {\n        var sql = \"select * from db1.test where a = ?\";\n        assertEquals(\"SELECT * FROM db1.test_r WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n        sql = \"select * from db1.`test` where a = ?\";\n        assertEquals(\"SELECT * FROM db1.`test_r` WHERE a = ?\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test8() {\n        // 这种jsql解析不了的\n        var sql = \"SELECT * FROM [HR].[dbo].[Employee_Salary_2023];\";\n        assertEquals(\"SELECT * FROM [HR].[dbo].[Employee_Salary_2023_r];\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test9(){\n        // 这种jsql解析不了的\n        var sql = \"\"\"\n            SELECT * FROM [SalesDB].[dbo].[Orders]\n            JOIN [MarketingDB].[dbo].[Customers]\\s\n            ON Orders.CustomerID = Customers.CustomerID;\n            \"\"\";\n        assertEquals(\"SELECT * FROM [SalesDB].[dbo].[Orders_r]\\n\" +\n            \"JOIN [MarketingDB].[dbo].[Customers_r] \\n\" +\n            \"ON Orders.CustomerID = Customers.CustomerID;\\n\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test10() {\n        var sql = \"\"\"\n            SELECT * FROM ecommerce_orders\\s\n            PARTITION (p2022, p2023)\n            WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31';\n            \"\"\";\n        assertEquals(\"SELECT * FROM ecommerce_orders_r PARTITION(p2022, p2023) WHERE order_date BETWEEN '2022-01-01' AND '2023-12-31'\", interceptor.changeTable(sql));\n    }\n\n    @Test\n    void test11() {\n        var sql = \"\"\"\n            SELECT order_id, customer_id,amount,\n                  RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank\n                FROM orders;\n            \"\"\";\n        assertEquals(\"SELECT order_id, customer_id, amount, RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rank FROM orders_r\", interceptor.changeTable(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/IllegalSQLInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;\nimport com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;\nimport org.apache.ibatis.jdbc.SqlRunner;\nimport org.h2.jdbcx.JdbcDataSource;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n/**\n * @author miemie\n * @since 2022-04-11\n */\nclass IllegalSQLInnerInterceptorTest {\n\n    private final IllegalSQLInnerInterceptor interceptor = new IllegalSQLInnerInterceptor();\n\n    private static DataSource dataSource;\n\n    @BeforeAll\n    public static void beforeAll() throws SQLException {\n        var jdbcDataSource = new JdbcDataSource();\n        jdbcDataSource.setURL(\"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\");\n        jdbcDataSource.setPassword(\"\");\n        jdbcDataSource.setUser(\"sa\");\n        dataSource = jdbcDataSource;\n        Connection connection = jdbcDataSource.getConnection();\n        var sql = \"\"\"\n            CREATE TABLE T_DEMO (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index1` (`a`,`b`)\n            );\n            CREATE TABLE T_TEST (\n              `a` int DEFAULT NULL,\n              `b` int DEFAULT NULL,\n              `c` int DEFAULT NULL,\n              KEY `ab_index2` (`a`,`b`)\n            );\n            \"\"\";\n        SqlRunner sqlRunner = new SqlRunner(connection);\n        sqlRunner.run(sql);\n    }\n\n    @Test\n    void test() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT COUNT(*) AS total FROM t_user WHERE (client_id = ?)\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete from t_user set age = 18\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age != 1\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from t_user where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"update t_user set age = 1 where (age = 1 or name = 'test')\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where age = 1 or name = 'test'\", null));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"delete t_user where (age = 1 or name = 'test')\", null));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where  b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where `a` = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 1 and `b` = 2\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.`T_DEMO` where c = 3\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from test.T_DEMO where c = 3\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a INNER JOIN `test` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN PUBLIC.`T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM PUBLIC.`T_DEMO` a INNER JOIN PUBLIC.`T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM T_DEMO a INNER JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.a = 1\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"SELECT * FROM `T_DEMO` a LEFT JOIN `T_TEST` b ON a.a = b.a WHERE a.b = 1\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (c = 3 OR b = 2)\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where c = 3 OR b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = 3 AND (c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 OR b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) or b = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a in (1,3,2) AND b = 2\", dataSource.getConnection()));\n\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where (a = 3 AND c = 3 AND b = 2)\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` a INNER JOIN T_TEST b ON a.a = b.a where a.a = 3 AND (b.c = 3 OR b.b = 2)\", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where a != (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        //TODO 低版本这里的抛异常了.看着应该不用抛出\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select * from `T_DEMO` where a <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b >= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select * from `T_DEMO` where b <= (SELECT b FROM T_TEST limit 1) \", dataSource.getConnection()));\n    }\n    @Test\n    void testCount() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from (select count(*) from (select * from T_DEMO where a = 1 and `b` = 2) a) c\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO`) a \", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from (select * from `T_DEMO` where b = (SELECT b FROM T_TEST limit 1)) a \", dataSource.getConnection()));\n    }\n\n\n    @Test\n    void testCatalogAndSchemaName() {\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from TEST.PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertDoesNotThrow(() -> interceptor.parserSingle(\"select count(*) from PUBLIC.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        // 非同一模式,读不到索引的情况\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n        Assertions.assertThrows(MybatisPlusException.class, () -> interceptor.parserSingle(\"select count(*) from PUBLIC.DB.T_DEMO where a = 1 and `b` = 2\", dataSource.getConnection()));\n    }\n\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/MultiDataPermissionInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.toolkit.StringPool;\nimport com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;\nimport com.google.common.collect.HashBasedTable;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.schema.Table;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SQL多表场景的数据权限拦截器测试\n *\n * @author houkunlin\n * @since 3.5.2 +\n */\npublic class MultiDataPermissionInterceptorTest {\n    private static final Logger logger = LoggerFactory.getLogger(MultiDataPermissionInterceptorTest.class);\n    /**\n     * 这里可以理解为数据库配置的数据权限规则 SQL\n     */\n    private static final com.google.common.collect.Table<String, String, String> sqlSegmentMap;\n    private static final DataPermissionInterceptor interceptor;\n    private static final String TEST_1 = \"com.baomidou.userMapper.selectByUsername\";\n    private static final String TEST_2 = \"com.baomidou.userMapper.selectById\";\n    private static final String TEST_3 = \"com.baomidou.roleMapper.selectByCompanyId\";\n    private static final String TEST_4 = \"com.baomidou.roleMapper.selectById\";\n    private static final String TEST_5 = \"com.baomidou.roleMapper.selectByRoleId\";\n    private static final String TEST_6 = \"com.baomidou.roleMapper.selectUserInfo\";\n    private static final String TEST_7 = \"com.baomidou.roleMapper.summarySum\";\n    private static final String TEST_8_1 = \"com.baomidou.CustomMapper.selectByOnlyMyData\";\n    private static final String TEST_8_2 = \"com.baomidou.CustomMapper.selectByOnlyOrgData\";\n    private static final String TEST_8_3 = \"com.baomidou.CustomMapper.selectByOnlyDeptData\";\n    private static final String TEST_8_4 = \"com.baomidou.CustomMapper.selectByMyDataOrDeptData\";\n    private static final String TEST_8_5 = \"com.baomidou.CustomMapper.selectByMyData\";\n\n    static {\n        sqlSegmentMap = HashBasedTable.create();\n        sqlSegmentMap.put(TEST_1, \"sys_user\", \"username='123' or userId IN (1,2,3)\");\n        sqlSegmentMap.put(TEST_2, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_3, \"sys_role\", \"companyId in (1,2,3)\");\n        sqlSegmentMap.put(TEST_4, \"sys_role\", \"username like 'abc%'\");\n        sqlSegmentMap.put(TEST_5, \"sys_role\", \"id=1 and role_id in (select id from sys_role)\");\n        sqlSegmentMap.put(TEST_6, \"sys_user\", \"u.state=1 and u.amount > 1000\");\n        sqlSegmentMap.put(TEST_6, \"sys_user_role\", \"r.role_id=3 AND r.role_id IN (7,9,11)\");\n        sqlSegmentMap.put(TEST_7, \"`fund`\", \"a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111\");\n        sqlSegmentMap.put(TEST_7, \"`fund_month`\", \"b.fund_id = 2 AND b.month <= '2022-05'\");\n        sqlSegmentMap.put(TEST_8_1, \"fund\", \"user_id=1\");\n        sqlSegmentMap.put(TEST_8_2, \"fund\", \"org_id=1\");\n        sqlSegmentMap.put(TEST_8_3, \"fund\", \"dept_id=1\");\n        sqlSegmentMap.put(TEST_8_4, \"fund\", \"user_id=1 or dept_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table1\", \"u.user_id=1\");\n        sqlSegmentMap.put(TEST_8_5, \"table2\", \"u.dept_id=1\");\n        interceptor = new DataPermissionInterceptor(new MultiDataPermissionHandler() {\n\n            @Override\n            public Expression getSqlSegment(final Table table, final Expression where, final String mappedStatementId) {\n                try {\n                    String sqlSegment = sqlSegmentMap.get(mappedStatementId, table.getName());\n                    if (sqlSegment == null) {\n                        logger.info(\"{} {} AS {} : NOT FOUND\", mappedStatementId, table.getName(), table.getAlias());\n                        return null;\n                    }\n                    if (table.getAlias() != null) {\n                        // 替换表别名\n                        sqlSegment = sqlSegment.replaceAll(\"u\\\\.\", table.getAlias().getName() + StringPool.DOT);\n                    }\n                    Expression sqlSegmentExpression = CCJSqlParserUtil.parseCondExpression(sqlSegment);\n                    logger.info(\"{} {} AS {} : {}\", mappedStatementId, table.getName(), table.getAlias(), sqlSegmentExpression.toString());\n                    return sqlSegmentExpression;\n                } catch (JSQLParserException e) {\n                    logger.error(\"解析错误:\", e);\n                }\n                return null;\n            }\n        });\n    }\n\n    @Test\n    void test1() {\n        assertSql(TEST_1, \"select * from sys_user\",\n            \"SELECT * FROM sys_user WHERE username = '123' OR userId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test2() {\n        assertSql(TEST_2, \"select u.username from sys_user u join sys_user_role r on u.id=r.user_id where r.role_id=3\",\n            \"SELECT u.username FROM sys_user u JOIN sys_user_role r ON u.id = r.user_id WHERE r.role_id = 3 AND u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test3() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test3unionAll() {\n        assertSql(TEST_3, \"select * from sys_role where company_id=6 union all select * from sys_role where company_id=7\",\n            \"SELECT * FROM sys_role WHERE company_id = 6 AND companyId IN (1, 2, 3) UNION ALL SELECT * FROM sys_role WHERE company_id = 7 AND companyId IN (1, 2, 3)\");\n    }\n\n    @Test\n    void test4() {\n        assertSql(TEST_4, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND username LIKE 'abc%'\");\n    }\n\n    @Test\n    void test5() {\n        assertSql(TEST_5, \"select * from sys_role where id=3\",\n            \"SELECT * FROM sys_role WHERE id = 3 AND id = 1 AND role_id IN (SELECT id FROM sys_role)\");\n    }\n\n    @Test\n    void test6() {\n        // 显式指定 JOIN 类型时 JOIN 右侧表才能进行拼接条件\n        assertSql(TEST_6, \"select u.username from sys_user u LEFT join sys_user_role r on u.id=r.user_id\",\n            \"SELECT u.username FROM sys_user u LEFT JOIN sys_user_role r ON u.id = r.user_id AND r.role_id = 3 AND r.role_id IN (7, 9, 11) WHERE u.state = 1 AND u.amount > 1000\");\n    }\n\n    @Test\n    void test7() {\n        assertSql(TEST_7, \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = TRUE) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\",\n            \"SELECT c.doc AS title, sum(c.total_paid_amount) AS total_paid_amount, sum(c.balance_amount) AS balance_amount FROM (SELECT `a`.`id`, `a`.`doc`, `b`.`month`, `b`.`total_paid_amount`, `b`.`balance_amount`, row_number() OVER (PARTITION BY `a`.`id` ORDER BY `b`.`month` DESC) AS `row_index` FROM `fund` `a` LEFT JOIN `fund_month` `b` ON `a`.`id` = `b`.`fund_id` AND `b`.`submit` = TRUE AND b.fund_id = 2 AND b.month <= '2022-05' WHERE a.id = 1 AND a.year = 2022 AND a.create_user_id = 1111) c WHERE c.row_index = 1 GROUP BY title LIMIT 20\");\n    }\n\n    @Test\n    void test8() {\n        assertSql(TEST_8_1, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1\");\n        assertSql(TEST_8_2, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND org_id = 1\");\n        assertSql(TEST_8_3, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND dept_id = 1\");\n        assertSql(TEST_8_4, \"select * from fund where id=3\",\n            \"SELECT * FROM fund WHERE id = 3 AND user_id = 1 OR dept_id = 1\");\n        // 修改之前旧版的多表数据权限对这个SQL的表现形式：\n        // 输入 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\"\n        // 输出 \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid) SELECT * FROM temp\"\n        // 修改之后的多表数据权限对这个SQL的表现形式\n        assertSql(TEST_8_5, \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 on t1.uid = t2.uid) SELECT * FROM temp\",\n            \"WITH temp AS (SELECT t1.field1, t2.field2 FROM table1 t1 LEFT JOIN table2 t2 ON t1.uid = t2.uid AND t2.dept_id = 1 WHERE t1.user_id = 1) SELECT * FROM temp\");\n    }\n\n    void assertSql(String mappedStatementId, String sql, String targetSql) {\n        assertThat(interceptor.parserSingle(sql, mappedStatementId)).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/PaginationInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-06-28\n */\nclass PaginationInnerInterceptorTest {\n\n    private final PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();\n\n    @Test\n    void optimizeCount() {\n        /* 能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id\",\n            \"SELECT COUNT(*) AS total FROM user u\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id LEFT JOIN permission p on p.id = u.per_id WHERE u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u WHERE u.xx = ?\");\n\n        assertsCountSql(\"select distinct id from table order by id\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n\n        assertsCountSql(\"select distinct id from table\", \"SELECT COUNT(*) FROM (SELECT DISTINCT id FROM table) TOTAL\");\n    }\n\n    @Test\n    void notOptimizeCount() {\n        /* 不能进行优化的 SQL */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? where u.xx = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id AND r.name = ? WHERE u.xx = ?\");\n\n        /* join 表与 where 条件大小写不同的情况 */\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id where R.NAME = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE R.NAME = ?\");\n\n        assertsCountSql(\"select * from user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\",\n            \"SELECT COUNT(*) AS total FROM user u LEFT JOIN role r ON r.id = u.role_id WHERE u.xax = ? AND r.cc = ? AND r.qq = ?\");\n    }\n\n    @Test\n    void optimizeCountOrderBy() {\n        /* order by 里不带参数,去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY name\",\n            \"SELECT COUNT(*) AS total FROM comment\");\n\n        /* order by 里带参数,不去除order by */\n        assertsCountSql(\"SELECT * FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\",\n            \"SELECT COUNT(*) AS total FROM comment ORDER BY (CASE WHEN creator = ? THEN 0 ELSE 1 END)\");\n    }\n\n    @Test\n    void withAsCount() {\n        assertsCountSql(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT COUNT(*) AS total FROM A\");\n    }\n\n    @Test\n    void withAsOrderBy() {\n        assertsConcatOrderBy(\"with A as (select * from class) select * from A\",\n            \"WITH A AS (SELECT * FROM class) SELECT * FROM A ORDER BY column ASC\",\n            OrderItem.asc(\"column\"));\n    }\n\n    @Test\n    void groupByCount() {\n        assertsCountSql(\"SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)\",\n            \"SELECT COUNT(*) FROM (SELECT * FROM record_1 WHERE id = ? GROUP BY date(date_time)) TOTAL\");\n    }\n\n    @Test\n    void leftJoinSelectCount() {\n        assertsCountSql(\"select r.id, r.name, r.phone,rlr.total_top_up from reseller r \" +\n                \"left join (select ral.reseller_id, sum(ral.top_up_money) as total_top_up, sum(ral.acquire_money) as total_acquire \" +\n                \"from reseller_acquire_log ral \" +\n                \"group by ral.reseller_id) rlr on r.id = rlr.reseller_id \" +\n                \"order by r.created_at desc\",\n            \"SELECT COUNT(*) AS total FROM reseller r\");\n\n        // 不优化\n        assertsCountSql(\"SELECT f.ca, f.cb FROM table_a f LEFT JOIN \" +\n                \"(SELECT ca FROM table_b WHERE cc = ?) rf on rf.ca = f.ca\",\n            \"SELECT COUNT(*) AS total FROM table_a f LEFT JOIN (SELECT ca FROM table_b WHERE cc = ?) rf ON rf.ca = f.ca\");\n\n        assertsCountSql(\"select * from order_info left join (select count(1) from order_info where create_time between ? and ?) tt on 1=1 WHERE equipment_id=?\",\n            \"SELECT COUNT(*) AS total FROM order_info LEFT JOIN (SELECT count(1) FROM order_info WHERE create_time BETWEEN ? AND ?) tt ON 1 = 1 WHERE equipment_id = ?\");\n    }\n\n    void assertsCountSql(String sql, String targetSql) {\n        assertThat(interceptor.autoCountSql(new Page<>(), sql)).isEqualTo(targetSql);\n    }\n\n    void assertsConcatOrderBy(String sql, String targetSql, OrderItem... orderItems) {\n        assertThat(interceptor.concatOrderBy(sql, Arrays.asList(orderItems))).isEqualTo(targetSql);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/extension/plugins/inner/TenantLineInnerInterceptorTest.java",
    "content": "package com.baomidou.mybatisplus.test.extension.plugins.inner;\n\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport com.baomidou.mybatisplus.jsqlparser.enums.ExpressionAppendMode;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-07-30\n */\nclass TenantLineInnerInterceptorTest {\n\n    private static final Map<String, String> FIRS_RESULT_TMAP = new HashMap<>();\n\n    private static final Map<String, String> LAST_RESULT_TMAP = new HashMap<>();\n\n    static {\n        firstResultMap();\n        lastResultMap();\n    }\n\n    static void firstResultMap() {\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        FIRS_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        FIRS_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        FIRS_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE tenant_id = 1 AND id = ?\");\n\n        // set subSelect\n        FIRS_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?) WHERE e.tenant_id = 1 AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND (id = ? OR name = ?)\");\n\n        /* not */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE tenant_id = 1 AND NOT (id = ? OR name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE u.tenant_id = 1 AND NOT (u.id = ? OR u.name = ?)\");\n\n        /* in */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在最前\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n        // 在最后\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n        // 在中间\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = ? AND e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* inner not = */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?) AND e.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.tenant_id = 1 AND e.id = ? LIMIT 1)\");\n\n        /* NOT EXISTS */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* >= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <= */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        /* <> */\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.tenant_id = 1 AND e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.tenant_id = 1 AND e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.tenant_id = 1 AND e1.id = ?))\");\n\n        FIRS_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t2.tenant_id = 1 AND t1.col1 = t2.col1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.tenant_id = 1 AND e2.id = 1) = 1, e2.type, e1.type) AS type FROM entity e1 WHERE e1.tenant_id = 1 AND e1.id = ?\");\n\n        // left join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e.tenant_id = 1\");\n\n        // right join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e RIGHT JOIN entity1 e1 ON e1.id = e.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id WHERE e1.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN entity1 e1 ON e.tenant_id = 1 AND e1.id = e.id LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id RIGHT JOIN entity2 e2 ON e.tenant_id = 1 AND e1.id = e2.id WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id INNER JOIN entity2 e2 ON e.tenant_id = 1 AND e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) WHERE e2.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) WHERE e1.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) RIGHT JOIN entity3 e3 ON e1.tenant_id = 1 AND e1.id = e3.id WHERE e3.tenant_id = 1\");\n\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.tenant_id = 1 AND e1.id = e2.id) ON e2.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e1.tenant_id = 1 AND e.id = e2.id WHERE e.tenant_id = 1\");\n\n        FIRS_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e1.id = e2.id) ON e.tenant_id = 1 AND e.id = e2.id WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN entity2 e2 ON e2.tenant_id = 1 AND e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e LEFT JOIN entity1 e1 LEFT JOIN with_as_A e2 ON e2.id = e1.id ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.NAME = ?)\");\n\n        // inner join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE e.id = ? OR e.name = ?\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN entity1 e1 ON e.tenant_id = 1 AND e1.tenant_id = 1 AND e1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e INNER JOIN with_as_1 w1 ON e.tenant_id = 1 AND w1.id = e.id WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b WHERE a.tenant_id = 1 AND a.id = b.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1 AND e.id = e1.id AND e.id = e2.id\");\n\n        // 沙雕的括号写法\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) WHERE e.tenant_id = 1 AND e1.tenant_id = 1 AND e.id = e1.id\");\n\n        // join\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.tenant_id = 1 AND e1.id = e.id WHERE e.tenant_id = 1 AND (e.id = ? OR e.name = ?)\");\n\n        FIRS_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        FIRS_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n    static void lastResultMap() {\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1)\");\n        // batch\n        LAST_RESULT_TMAP.put(\"insert into entity (id) values (?),(?)\",\n            \"INSERT INTO entity (id, tenant_id) VALUES (?, 1), (?, 1)\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) values (?,?),(?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, 1), (?, ?, 1)\");\n        // 无 insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity value (?,?)\",\n            \"INSERT INTO entity VALUES (?, ?)\");\n        // 自己加了insert的列\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name,tenant_id) value (?,?,?)\",\n            \"INSERT INTO entity (id, name, tenant_id) VALUES (?, ?, ?)\");\n        // insert into select\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from entity2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM entity2 WHERE tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from entity2 e2\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT id, name, tenant_id FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT * FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n        LAST_RESULT_TMAP.put(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\",\n            \"INSERT INTO entity (id, name, tenant_id) SELECT t.* FROM (SELECT id, name, tenant_id FROM entity3 e3 WHERE e3.tenant_id = 1) t\");\n\n        LAST_RESULT_TMAP.put(\"delete from entity where id = ?\", \"DELETE FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"update entity set name = ? where id = ?\",\n            \"UPDATE entity SET name = ? WHERE id = ? AND tenant_id = 1\");\n\n        // set subSelect\n        LAST_RESULT_TMAP.put(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\",\n            \"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1) \" +\n                \"WHERE e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\",\n            \"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test' WHERE tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\",\n            \"UPDATE entity t1 INNER JOIN entity t2 ON t1.a = t2.a SET t1.b = t2.b, t1.c = t2.c WHERE t1.tenant_id = 1\");\n\n        // 单表\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ?\", \"SELECT * FROM entity WHERE id = ? AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity where id = ? or name = ?\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1\");\n\n        /* not */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", \"SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", \"SELECT * FROM entity u WHERE NOT (u.id = ? OR u.name = ?) AND u.tenant_id = 1\");\n\n        /* in */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id IN (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在最前\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n        // 在最后\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n        // 在中间\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n                \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\",\n            \"SELECT * FROM entity e WHERE e.id = ? AND e.id IN \" +\n                \"(SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ? AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* inner not = */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1)) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT (e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.id = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", \"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? AND e.tenant_id = 1 LIMIT 1)\");\n\n        /* NOT EXISTS */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE NOT EXISTS (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* >= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id >= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n\n        /* <= */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <= (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        /* <> */\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\",\n            \"SELECT * FROM entity e WHERE e.id <> (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\",\n            \"SELECT * FROM (SELECT e.id FROM entity e WHERE e.id = (SELECT e1.id FROM entity1 e1 WHERE e1.id = ? AND e1.tenant_id = 1) AND e.tenant_id = 1)\");\n\n        LAST_RESULT_TMAP.put(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\",\n            \"SELECT t1.col1, (SELECT t2.col2 FROM t2 t2 WHERE t1.col1 = t2.col1 AND t2.tenant_id = 1) FROM t1 t1 WHERE t1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ?\",\n            \"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1 AND e2.tenant_id = 1) = 1, e2.type, e1.type) AS type \" +\n                \"FROM entity e1 WHERE e1.id = ? AND e1.tenant_id = 1\");\n\n        // left join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        // right join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            \"SELECT * FROM with_as_1 e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?) AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"INNER JOIN entity2 e2 ON e1.id = e2.id AND e.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from (select * from entity e) e1 \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            \"SELECT * FROM (SELECT * FROM entity e WHERE e.tenant_id = 1) e1 \" +\n                \"LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * from entity1 e1 \" +\n                \"left join (select * from entity2 e2) e22 \" +\n                \"on e1.id = e22.id\",\n            \"SELECT * FROM entity1 e1 \" +\n                \"LEFT JOIN (SELECT * FROM entity2 e2 WHERE e2.tenant_id = 1) e22 \" +\n                \"ON e1.id = e22.id \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"WHERE e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"WHERE e1.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n                \"right join entity3 e3 on e1.id = e3.id\",\n            \"SELECT * FROM \" +\n                \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"RIGHT JOIN entity3 e3 ON e1.id = e3.id AND e1.tenant_id = 1 \" +\n                \"WHERE e3.tenant_id = 1\");\n\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 RIGHT JOIN entity2 e2 ON e1.id = e2.id AND e1.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e2.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e1.tenant_id = 1 \" +\n                \"WHERE e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"select * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n                \"on e.id = e2.id\",\n            \"SELECT * FROM entity e \" +\n                \"RIGHT JOIN (entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id AND e2.tenant_id = 1) \" +\n                \"ON e.id = e2.id AND e.tenant_id = 1 \" +\n                \"WHERE e1.tenant_id = 1\");\n\n        // 多个 on 尾缀的\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN entity2 e2 ON e2.id = e1.id AND e2.tenant_id = 1 \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.NAME = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"LEFT JOIN entity1 e1 \" +\n                \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n                \"ON e1.id = e.id AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.NAME = ?) AND e.tenant_id = 1\");\n\n        // inner join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE e.id = ? OR e.name = ?\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join entity1 e1 on e1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN entity1 e1 ON e1.id = e.id AND e.tenant_id = 1 AND e1.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // ignore table\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e \" +\n                \"inner join with_as_1 w1 on w1.id = e.id \" +\n                \"WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e \" +\n                \"INNER JOIN with_as_1 w1 ON w1.id = e.id AND e.tenant_id = 1 \" +\n                \"WHERE (e.id = ? OR e.name = ?)\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e,entity1 e1 \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM entity e, entity1 e1 \" +\n                \"WHERE e.id = e1.id AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id AND a.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\",\n            \"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n                \"WHERE a.id = b.id\");\n\n        // SubJoin with 隐式内连接\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,entity1 e1) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (entity e, entity1 e1) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM ((entity e, entity1 e1), entity2 e2) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n                \"WHERE e.id = e1.id and e.id = e2.id\",\n            \"SELECT * FROM (entity e, (entity1 e1, entity2 e2)) \" +\n                \"WHERE e.id = e1.id AND e.id = e2.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1 AND e2.tenant_id = 1\");\n\n        // 沙雕的括号写法\n        LAST_RESULT_TMAP.put(\"SELECT * FROM (((entity e,entity1 e1))) \" +\n                \"WHERE e.id = e1.id\",\n            \"SELECT * FROM (((entity e, entity1 e1))) \" +\n                \"WHERE e.id = e1.id \" +\n                \"AND e.tenant_id = 1 AND e1.tenant_id = 1\");\n\n        // join\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\",\n            \"SELECT * FROM entity e JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 WHERE (e.id = ? OR e.name = ?) AND e.tenant_id = 1\");\n\n        LAST_RESULT_TMAP.put(\"with with_as_A as (select * from entity) select * from with_as_A\",\n            \"WITH with_as_A AS (SELECT * FROM entity WHERE tenant_id = 1) SELECT * FROM with_as_A\");\n\n        LAST_RESULT_TMAP.put(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\",\n            \"INSERT INTO entity (name, age, tenant_id) VALUES ('秋秋', 18, 1), ('秋秋', '22', 1) ON DUPLICATE KEY UPDATE age = 18, tenant_id = 1\");\n    }\n\n\n    private final TenantLineInnerInterceptor interceptor = new TenantLineInnerInterceptor(new TenantLineHandler() {\n        private boolean ignoreFirst;// 需要执行 getTenantId 前必须先执行 ignoreTable\n\n        @Override\n        public Expression getTenantId() {\n            assertThat(ignoreFirst).isEqualTo(true);\n            ignoreFirst = false;\n            return new LongValue(1);\n        }\n\n        @Override\n        public boolean ignoreTable(String tableName) {\n            ignoreFirst = true;\n            return tableName.startsWith(\"with_as\");\n        }\n    });\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    public void test(ExpressionAppendMode appendMode) {\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == appendMode ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertMap.forEach((k, v) -> assertSql(k, appendMode));\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void insert(ExpressionAppendMode appendMode) {\n        // plain\n        assertSql(\"insert into entity (id) values (?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?)\", appendMode);\n        // batch\n        assertSql(\"insert into entity (id) values (?),(?)\", appendMode);\n        assertSql(\"insert into entity (id,name) values (?,?),(?,?)\", appendMode);\n        // 无 insert的列\n        assertSql(\"insert into entity value (?,?)\", appendMode);\n        // 自己加了insert的列\n        assertSql(\"insert into entity (id,name,tenant_id) value (?,?,?)\", appendMode);\n        // insert into select\n        assertSql(\"insert into entity (id,name) select id,name from entity2\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from entity2 e2\", appendMode);\n        assertSql(\"insert into entity (id,name) select id,name from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select * from (select id,name from entity3 e3) t\", appendMode);\n        assertSql(\"insert into entity (id,name) select t.* from (select id,name from entity3 e3) t\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void delete(ExpressionAppendMode appendMode) {\n        assertSql(\"delete from entity where id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void update(ExpressionAppendMode appendMode) {\n        assertSql(\"update entity set name = ? where id = ?\", appendMode);\n        // set subSelect\n        assertSql(\"UPDATE entity e SET e.cq = (SELECT e1.total FROM entity e1 WHERE e1.id = ?) WHERE e.id = ?\", appendMode);\n\n        assertSql(\"UPDATE sys_user SET (name, age) = ('秋秋', 18), address = 'test'\", appendMode);\n\n        assertSql(\"UPDATE entity t1 INNER JOIN entity t2 ON t1.a= t2.a SET t1.b = t2.b, t1.c = t2.c\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingle(ExpressionAppendMode appendMode) {\n        // 单表\n        assertSql(\"select * from entity where id = ?\", appendMode);\n\n        assertSql(\"select * from entity where id = ? or name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity WHERE (id = ? OR name = ?)\", appendMode);\n\n        /* not */\n        assertSql(\"SELECT * FROM entity WHERE not (id = ? OR name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity u WHERE not (u.id = ? OR u.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectIn(ExpressionAppendMode appendMode) {\n        /* in */\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在最前\n        assertSql(\"SELECT * FROM entity e WHERE e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n        // 在最后\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        // 在中间\n        assertSql(\"SELECT * FROM entity e WHERE e.id = ? and e.id IN \" +\n            \"(select e1.id from entity1 e1 where e1.id = ?) and e.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectEq(ExpressionAppendMode appendMode) {\n        /* = */\n        assertSql(\"SELECT * FROM entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectInnerNotEq(ExpressionAppendMode appendMode) {\n        /* inner not = */\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e WHERE not (e.id = (select e1.id from entity1 e1 where e1.id = ?) and e.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubSelectExists(ExpressionAppendMode appendMode) {\n        /* EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n\n        assertSql(\"SELECT EXISTS (SELECT 1 FROM entity1 e WHERE e.id = ? LIMIT 1)\", appendMode);\n\n        /* NOT EXISTS */\n        assertSql(\"SELECT * FROM entity e WHERE NOT EXISTS (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWhereSubSelect(ExpressionAppendMode appendMode) {\n        /* >= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id >= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <= */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <= (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n        /* <> */\n        assertSql(\"SELECT * FROM entity e WHERE e.id <> (select e1.id from entity1 e1 where e1.id = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectFromSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM (select e.id from entity e WHERE e.id = (select e1.id from entity1 e1 where e1.id = ?))\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodySubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select t1.col1,(select t2.col2 from t2 t2 where t1.col1=t2.col1) from t1 t1\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectBodyFuncSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT e1.*, IF((SELECT e2.id FROM entity2 e2 WHERE e2.id = 1) = 1, e2.type, e1.type) AS type \" +\n            \"FROM entity e1 WHERE e1.id = ?\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoin(ExpressionAppendMode appendMode) {\n        // left join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"left join entity1 e1 on e1.id = e.id \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectRightJoin(ExpressionAppendMode appendMode) {\n        // right join\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM with_as_1 e \" +\n                \"right join entity1 e1 on e1.id = e.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"WHERE e.id = ? OR e.name = ?\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id \",\n            appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectMixJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"SELECT * FROM entity e \" +\n                \"right join entity1 e1 on e1.id = e.id \" +\n                \"left join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"right join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n                \"left join entity1 e1 on e1.id = e.id \" +\n                \"inner join entity2 e2 on e1.id = e2.id\",\n            appendMode);\n    }\n\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectJoinSubSelect(ExpressionAppendMode appendMode) {\n        assertSql(\"select * from (select * from entity e) e1 \" +\n            \"left join entity2 e2 on e1.id = e2.id\", appendMode);\n\n        assertSql(\"select * from entity1 e1 \" +\n            \"left join (select * from entity2 e2) e22 \" +\n            \"on e1.id = e22.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSubJoin(ExpressionAppendMode appendMode) {\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 right JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id)\", appendMode);\n\n\n        assertSql(\"select * FROM \" +\n            \"(entity1 e1 LEFT JOIN entity2 e2 ON e1.id = e2.id) \" +\n            \"right join entity3 e3 on e1.id = e3.id\", appendMode);\n\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 right join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"LEFT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n\n        assertSql(\"select * FROM entity e \" +\n            \"RIGHT JOIN (entity1 e1 left join entity2 e2 ON e1.id = e2.id) \" +\n            \"on e.id = e2.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectLeftJoinMultipleTrailingOn(ExpressionAppendMode appendMode) {\n        // 多个 on 尾缀的\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN entity2 e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"LEFT JOIN entity1 e1 \" +\n            \"LEFT JOIN with_as_A e2 ON e2.id = e1.id \" +\n            \"ON e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.NAME = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectInnerJoin(ExpressionAppendMode appendMode) {\n        // inner join\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join entity1 e1 on e1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // ignore table\n        assertSql(\"SELECT * FROM entity e \" +\n            \"inner join with_as_1 w1 on w1.id = e.id \" +\n            \"WHERE (e.id = ? OR e.name = ?)\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity e,entity1 e1 \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        // 隐式内连接\n        assertSql(\"SELECT * FROM entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        assertSql(\"SELECT * FROM with_as_entity a, with_as_entity1 b \" +\n            \"WHERE a.id = b.id\", appendMode);\n\n        // SubJoin with 隐式内连接\n        assertSql(\"SELECT * FROM (entity e,entity1 e1) \" +\n            \"WHERE e.id = e1.id\", appendMode);\n\n        assertSql(\"SELECT * FROM ((entity e,entity1 e1),entity2 e2) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        assertSql(\"SELECT * FROM (entity e,(entity1 e1,entity2 e2)) \" +\n            \"WHERE e.id = e1.id and e.id = e2.id\", appendMode);\n\n        // 沙雕的括号写法\n        assertSql(\"SELECT * FROM (((entity e,entity1 e1))) WHERE e.id = e1.id\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectSingleJoin(ExpressionAppendMode appendMode) {\n        // join\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE e.id = ? OR e.name = ?\", appendMode);\n\n        assertSql(\"SELECT * FROM entity e join entity1 e1 on e1.id = e.id WHERE (e.id = ? OR e.name = ?)\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void selectWithAs(ExpressionAppendMode appendMode) {\n        assertSql(\"with with_as_A as (select * from entity) select * from with_as_A\", appendMode);\n    }\n\n    @ParameterizedTest\n    @EnumSource(ExpressionAppendMode.class)\n    void testDuplicateKeyUpdate(ExpressionAppendMode appendMode) {\n        assertSql(\"INSERT INTO entity (name,age) VALUES ('秋秋',18),('秋秋','22') ON DUPLICATE KEY UPDATE age=18\", appendMode);\n    }\n\n    void assertSql(String sql, ExpressionAppendMode appendMode) {\n        interceptor.setExpressionAppendMode(appendMode);\n        Map<String, String> assertMap = ExpressionAppendMode.LAST == interceptor.getExpressionAppendMode() ? LAST_RESULT_TMAP : FIRS_RESULT_TMAP;\n        assertThat(interceptor.parserSingle(sql, null)).isEqualTo(assertMap.get(sql));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-5.0/src/test/java/com/baomidou/mybatisplus/test/pagination/SelectBodyToPlainSelectTest.java",
    "content": "package com.baomidou.mybatisplus.test.pagination;\n\nimport com.baomidou.mybatisplus.core.metadata.OrderItem;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport net.sf.jsqlparser.JSQLParserException;\nimport net.sf.jsqlparser.parser.CCJSqlParserUtil;\nimport net.sf.jsqlparser.statement.select.PlainSelect;\nimport net.sf.jsqlparser.statement.select.Select;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * SelectBody强转PlainSelect不支持sql里面最外层带union\n * 用SetOperationList处理sql带union的语句\n */\nclass SelectBodyToPlainSelectTest {\n\n    private static final List<OrderItem> ITEMS = new ArrayList<>();\n\n    static {\n        ITEMS.add(OrderItem.asc(\"column\"));\n    }\n\n    /**\n     * 报错的测试\n     */\n    @Test\n    void testSelectBodyToPlainSelectThrowException() {\n        Select selectStatement = null;\n        try {\n            String originalUnionSql = \"select * from test union select * from test\";\n            selectStatement = (Select) CCJSqlParserUtil.parse(originalUnionSql);\n        } catch (JSQLParserException e) {\n            e.printStackTrace();\n        }\n        assert selectStatement != null;\n        Select finalSelectStatement = selectStatement;\n        Assertions.assertThrows(ClassCastException.class, () -> {\n            PlainSelect plainSelect = (PlainSelect) finalSelectStatement.getSelectBody();\n        });\n    }\n\n    @BeforeEach\n    void setup() {\n        List<OrderItem> orderItems = new ArrayList<>();\n        OrderItem order = new OrderItem();\n        order.setAsc(true);\n        order.setColumn(\"column\");\n        orderItems.add(order);\n        OrderItem orderEmptyColumn = new OrderItem();\n        orderEmptyColumn.setAsc(false);\n        orderEmptyColumn.setColumn(\"\");\n        orderItems.add(orderEmptyColumn);\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByBefore() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFix() {\n        List<OrderItem> orderList = new ArrayList<>();\n        // 测试可能的 sql 注入 https://github.com/baomidou/mybatis-plus/issues/5745\n        orderList.add(OrderItem.asc(\"col umn\"));\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union select * from test2\", orderList);\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test UNION SELECT * FROM test2 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test union all select * from test2\", orderList);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test UNION ALL SELECT * FROM test2 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorConcatOrderByFixWithWhere() {\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union select * from test2 where 1 = 1\", ITEMS);\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n\n        String actualSqlUnionAll = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1 union all select * from test2 where 1 = 1 \", ITEMS);\n        assertThat(actualSqlUnionAll).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 UNION ALL SELECT * FROM test2 WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n    @Test\n    void testPaginationInterceptorOrderByEmptyColumnFix() {\n        String actualSql = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test\", ITEMS);\n\n        assertThat(actualSql).isEqualTo(\"SELECT * FROM test ORDER BY column ASC\");\n\n        String actualSqlWhere = new PaginationInnerInterceptor()\n            .concatOrderBy(\"select * from test where 1 = 1\", ITEMS);\n\n        assertThat(actualSqlWhere).isEqualTo(\"SELECT * FROM test WHERE 1 = 1 ORDER BY column ASC\");\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-common/build.gradle",
    "content": "dependencies {\n    api project(\":mybatis-plus-extension\")\n    implementation \"${lib.\"slf4j-api\"}\"\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-common/src/main/java/com/baomidou/mybatisplus/jsqlparser/JsqlParserThreadPool.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.jsqlparser;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * JsqlParser解析线程池\n * <p>当没指定解析线程池时,默认使用一个固定长度的线程作为解析线程池.</p>\n *\n * @author nieqiurong\n * @since 3.5.11\n */\npublic class JsqlParserThreadPool {\n\n    /**\n     * 默认线程数大小\n     */\n    public static final int DEFAULT_THREAD_SIZE = (Runtime.getRuntime().availableProcessors() + 1) / 2;\n\n    /**\n     * 获取默认解析线程池(固定大小)\n     *\n     * @return 解析线程池\n     */\n    public static ExecutorService getDefaultThreadPoolExecutor() {\n        return DefaultJsqlParserFixedThreadPool.INSTANCE.getDefaultThreadPoolExecutor();\n    }\n\n    /**\n     * 注册Jvm退出钩子\n     *\n     * @param executorService 线程池\n     * @deprecated 3.5.12\n     */\n    @Deprecated\n    public static void addShutdownHook(ExecutorService executorService) {\n        Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n            if (!executorService.isShutdown()) {\n                executorService.shutdown();\n            }\n        }, \"mybatis-plus-jsqlParser-shutdown-hook\"));\n    }\n\n    private static class DefaultJsqlParserFixedThreadPool {\n\n        private static final DefaultJsqlParserFixedThreadPool INSTANCE = new DefaultJsqlParserFixedThreadPool();\n\n        public static ExecutorService executorService = new ThreadPoolExecutor(DEFAULT_THREAD_SIZE, DEFAULT_THREAD_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> {\n            Thread thread = new Thread(r);\n            thread.setName(\"mybatis-plus-jsqlParser-\" + thread.getId());\n            thread.setDaemon(true);\n            return thread;\n        });\n\n        /**\n         * 默认解析线程池(固定大小,默认大小{@link #DEFAULT_THREAD_SIZE})\n         *\n         * @return 线程池\n         */\n        public ExecutorService getDefaultThreadPoolExecutor() {\n            return executorService;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-jsqlparser-support/mybatis-plus-jsqlparser-common/src/main/java/com/baomidou/mybatisplus/jsqlparser/enums/ExpressionAppendMode.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.jsqlparser.enums;\n\n/**\n * 表达式追加模式\n *\n * @author nieqiurong\n * @since 3.5.11\n */\npublic enum ExpressionAppendMode {\n\n    /**\n     * 追加至首位\n     */\n    FIRST,\n\n    /**\n     * 追加至末尾\n     */\n    LAST\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/build.gradle",
    "content": "apply plugin: 'kotlin'\n\ncompileKotlin {\n    kotlinOptions {\n        jvmTarget = \"1.8\"\n    }\n}\n\ncompileTestKotlin {\n    kotlinOptions {\n        freeCompilerArgs = ['-Xjvm-default=all']\n    }\n}\n\ndependencies {\n    api project(\":mybatis-plus-extension\")\n    implementation \"${lib.\"mybatis-spring\"}\"\n\n    implementation \"${lib.\"kotlin-stdlib-jdk8\"}\"\n    implementation \"${lib.\"kotlin-reflect\"}\"\n    implementation \"${lib.\"spring-context-support\"}\"\n    implementation \"${lib.\"spring-jdbc\"}\"\n    implementation \"${lib.\"slf4j-api\"}\"\n    testImplementation \"${lib.h2}\"\n    testImplementation \"${lib.mysql}\"\n    testImplementation \"${lib.'logback-classic'}\"\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/activerecord/Model.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.activerecord;\n\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner;\n\n/**\n * ActiveRecord 模式 CRUD\n * <p>\n * 必须存在对应的原始mapper并继承baseMapper并且可以使用的前提下\n * 才能使用此 AR 模式 !!!\n * </p>\n *\n * @param <T>\n * @author hubin\n * @since 2016-11-06\n */\npublic abstract class Model<T extends Model<?>> extends AbstractModel<T> {\n\n    /**\n     * 执行 SQL\n     */\n    public SqlRunner sql() {\n        return new SqlRunner(this.entityClass);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/ddl/SimpleDdl.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.ddl;\n\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport javax.sql.DataSource;\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * 非多数据源 DDL 实现\n *\n * @author hubin\n * @since 2021-09-23\n */\npublic class SimpleDdl implements IDdl {\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Override\n    public void runScript(Consumer<DataSource> consumer) {\n        consumer.accept(dataSource);\n    }\n\n    @Override\n    public List<String> getSqlFiles() {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/repository/CrudRepository.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.repository;\n\nimport com.baomidou.mybatisplus.core.enums.SqlMethod;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Collection;\n\n/**\n * IService 实现类（ 泛型：M 是 mapper 对象，T 是实体 ）\n *\n * @author hubin\n * @since 2018-06-23\n */\npublic abstract class CrudRepository<M extends BaseMapper<T>, T> extends AbstractRepository<M, T> {\n\n    @Autowired\n    protected M baseMapper;\n\n    @Override\n    public M getBaseMapper() {\n        Assert.notNull(this.baseMapper, \"baseMapper can not be null\");\n        return this.baseMapper;\n    }\n\n    /**\n     * 批量插入\n     *\n     * @param entityList ignore\n     * @param batchSize  ignore\n     * @return ignore\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public boolean saveBatch(Collection<T> entityList, int batchSize) {\n        String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);\n        return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));\n    }\n\n    /**\n     * 获取mapperStatementId\n     *\n     * @param sqlMethod 方法名\n     * @return 命名id\n     * @since 3.4.0\n     */\n    protected String getSqlStatement(SqlMethod sqlMethod) {\n        return SqlHelper.getSqlStatement(this.getMapperClass(), sqlMethod);\n    }\n\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());\n        Assert.notNull(tableInfo, \"error: can not execute. because can not find cache of TableInfo for entity!\");\n        String keyProperty = tableInfo.getKeyProperty();\n        Assert.notEmpty(keyProperty, \"error: can not execute. because can not find column for id from entity!\");\n        return SqlHelper.saveOrUpdateBatch(getSqlSessionFactory(), this.getMapperClass(), this.log, entityList, batchSize, (sqlSession, entity) -> {\n            Object idVal = tableInfo.getPropertyValue(entity, keyProperty);\n            return StringUtils.checkValNull(idVal)\n                || CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));\n        }, (sqlSession, entity) -> {\n            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();\n            param.put(Constants.ENTITY, entity);\n            sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);\n        });\n    }\n\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public boolean updateBatchById(Collection<T> entityList, int batchSize) {\n        String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);\n        return executeBatch(entityList, batchSize, (sqlSession, entity) -> {\n            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();\n            param.put(Constants.ENTITY, entity);\n            sqlSession.update(sqlStatement, param);\n        });\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/service/IService.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.service;\n\nimport com.baomidou.mybatisplus.extension.repository.IRepository;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.Collection;\n\n/**\n * 顶级 Service\n * <p>\n *\n * </p>\n *\n * @author hubin\n * @since 2018-06-23\n */\npublic interface IService<T> extends IRepository<T> {\n\n    /**\n     * 插入（批量）\n     *\n     * @param entityList 实体对象集合\n     */\n    @Transactional(rollbackFor = Exception.class)\n    default boolean saveBatch(Collection<T> entityList) {\n        return saveBatch(entityList, DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 批量修改插入\n     *\n     * @param entityList 实体对象集合\n     */\n    @Transactional(rollbackFor = Exception.class)\n    default boolean saveOrUpdateBatch(Collection<T> entityList) {\n        return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);\n    }\n\n    /**\n     * 批量删除(jdbc批量提交)\n     *\n     * @param list 主键ID或实体列表(主键ID类型必须与实体类型字段保持一致)\n     * @return 删除结果\n     * @since 3.5.0\n     */\n    @Transactional(rollbackFor = Exception.class)\n    default boolean removeBatchByIds(Collection<?> list) {\n        return removeByIds(list);\n    }\n\n    /**\n     * 根据ID 批量更新\n     *\n     * @param entityList 实体对象集合\n     */\n    @Transactional(rollbackFor = Exception.class)\n    default boolean updateBatchById(Collection<T> entityList) {\n        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/service/impl/ServiceImpl.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.service.impl;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.extension.repository.CrudRepository;\nimport com.baomidou.mybatisplus.extension.service.IService;\n\n/**\n * IService 实现类（ 泛型：M 是 mapper 对象，T 是实体 ）\n *\n * @author hubin\n * @since 2018-06-23\n */\npublic class ServiceImpl<M extends BaseMapper<T>, T> extends CrudRepository<M, T> implements IService<T> {\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/service/impl/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 通用 service 实现\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.service.impl;\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/service/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * 通用 Service 接口\n *\n * @author hubin\n * @since 2018-06-08\n */\npackage com.baomidou.mybatisplus.extension.service;\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/spi/SpringCompatibleSet.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.spi;\n\nimport com.baomidou.mybatisplus.core.toolkit.AopUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;\nimport lombok.SneakyThrows;\nimport org.apache.ibatis.exceptions.PersistenceException;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.reflection.ExceptionUtil;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.MyBatisExceptionTranslator;\nimport org.mybatis.spring.SqlSessionHolder;\nimport org.mybatis.spring.SqlSessionUtils;\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.transaction.support.TransactionSynchronizationManager;\n\nimport java.io.InputStream;\nimport java.util.function.Consumer;\n\n/**\n * spring 兼容方法集接口实现类\n */\npublic class SpringCompatibleSet implements CompatibleSet {\n\n    private static final Log LOG = LogFactory.getLog(SpringCompatibleSet.class);\n\n    public static volatile ApplicationContext applicationContext;\n\n    @Override\n    public SqlSession getSqlSession(SqlSessionFactory sessionFactory) {\n        return SqlSessionUtils.getSqlSession(sessionFactory);\n    }\n\n    @Override\n    public void closeSqlSession(SqlSession sqlSession, SqlSessionFactory sqlSessionFactory) {\n        SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);\n    }\n\n    @SneakyThrows\n    @Override\n    public boolean executeBatch(SqlSessionFactory sqlSessionFactory, Log log, Consumer<SqlSession> consumer) {\n        SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);\n        boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();\n        if (sqlSessionHolder != null) {\n            SqlSession sqlSession = sqlSessionHolder.getSqlSession();\n            //原生无法支持执行器切换，当存在批量操作时，会嵌套两个session的，优先commit上一个session\n            //按道理来说，这里的值应该一直为false。\n            sqlSession.commit(!transaction);\n        }\n        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);\n        if (!transaction) {\n            log.warn(\"SqlSession [\" + sqlSession + \"] Transaction not enabled\");\n        }\n        try {\n            consumer.accept(sqlSession);\n            //非事务情况下，强制commit。\n            sqlSession.commit(!transaction);\n            return true;\n        } catch (Throwable t) {\n            sqlSession.rollback();\n            Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);\n            if (unwrapped instanceof PersistenceException) {\n                MyBatisExceptionTranslator myBatisExceptionTranslator\n                    = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);\n                Throwable throwable = myBatisExceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);\n                if (throwable != null) {\n                    throw throwable;\n                }\n            }\n            throw ExceptionUtils.mpe(unwrapped);\n        } finally {\n            if (!transaction) {\n                sqlSession.close();\n            }\n        }\n    }\n\n    @Override\n    public InputStream getInputStream(String path) throws Exception {\n        return new ClassPathResource(path).getInputStream();\n    }\n\n    @Override\n    public <T> T getBean(Class<T> clz) {\n        if (applicationContext != null) {\n            ObjectProvider<T> provider = applicationContext.getBeanProvider(clz);\n            return provider.getIfAvailable();\n        }\n        LOG.warn(\"The applicationContext property is empty. Please initialize it via the static field of applicationContext in SpringContextHolder or by calling the setApplicationContext method of MybatisSqlSessionFactoryBean.\");\n        return null;\n    }\n\n    @Override\n    public Object getProxyTargetObject(Object mapper) {\n        Object result = mapper;\n        if (AopUtils.isLoadSpringAop()) {\n            while (org.springframework.aop.support.AopUtils.isAopProxy(result)) {\n                result = AopProxyUtils.getSingletonTarget(result);\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public void setContext(Object context) {\n        applicationContext = (ApplicationContext) context;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/spring/MybatisPlusApplicationContextAware.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.spring;\n\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport com.baomidou.mybatisplus.core.toolkit.Assert;\nimport org.jetbrains.annotations.NotNull;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\n\n/**\n * Spring容器访问\n *\n * @author nieqiurong\n * @since 3.5.12\n * @deprecated 3.5.13 初始化顺序不太好兼容Bean初始化方法执行逻辑，使用{@link MybatisSqlSessionFactoryBean#setApplicationContext(ApplicationContext)}替代.\n */\n@Deprecated\npublic class MybatisPlusApplicationContextAware implements ApplicationContextAware {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisPlusApplicationContextAware.class);\n\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {\n        LOGGER.info(\"Register ApplicationContext instances {}\", applicationContext.getDisplayName());\n        MybatisPlusApplicationContextAware.applicationContext = applicationContext;\n        if (CompatibleHelper.hasCompatibleSet()) {\n            CompatibleHelper.getCompatibleSet().setContext(applicationContext);\n        }\n    }\n\n    public static boolean hasApplicationContext() {\n        return applicationContext != null;\n    }\n\n    public static ApplicationContext getApplicationContext() {\n        Assert.isTrue(hasApplicationContext(), \"applicationContext is null\");\n        return applicationContext;\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.spring;\n\nimport com.baomidou.mybatisplus.core.*;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.spi.CompatibleHelper;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport com.baomidou.mybatisplus.extension.toolkit.SqlHelper;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.apache.ibatis.cache.Cache;\nimport org.apache.ibatis.executor.ErrorContext;\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.io.VFS;\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.apache.ibatis.mapping.Environment;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.reflection.factory.ObjectFactory;\nimport org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.mybatis.logging.Logger;\nimport org.mybatis.logging.LoggerFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.transaction.SpringManagedTransactionFactory;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.event.ContextRefreshedEvent;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.core.type.ClassMetadata;\nimport org.springframework.core.type.classreading.CachingMetadataReaderFactory;\nimport org.springframework.core.type.classreading.MetadataReaderFactory;\nimport org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;\nimport org.springframework.util.ClassUtils;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.lang.reflect.Modifier;\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.function.IntFunction;\nimport java.util.stream.Stream;\n\nimport static org.springframework.util.Assert.notNull;\nimport static org.springframework.util.Assert.state;\nimport static org.springframework.util.ObjectUtils.isEmpty;\nimport static org.springframework.util.StringUtils.hasLength;\nimport static org.springframework.util.StringUtils.tokenizeToStringArray;\n\n/**\n * 拷贝类 {@link SqlSessionFactoryBean} 修改方法 buildSqlSessionFactory() 加载自定义\n * <p> MybatisXmlConfigBuilder </p>\n * <p> 移除 sqlSessionFactoryBuilder 属性,强制使用 `new MybatisSqlSessionFactoryBuilder()` </p>\n * <p> 移除 environment 属性,强制使用 `MybatisSqlSessionFactoryBean.class.getSimpleName()` </p>\n *\n * @author hubin\n * @since 2017-01-04\n */\npublic class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisSqlSessionFactoryBean.class);\n\n    private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();\n    private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();\n\n    private Resource configLocation;\n\n    private MybatisConfiguration configuration;\n\n    private Resource[] mapperLocations;\n\n    private DataSource dataSource;\n\n    private TransactionFactory transactionFactory;\n\n    private Properties configurationProperties;\n\n    private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new MybatisSqlSessionFactoryBuilder();\n\n    private SqlSessionFactory sqlSessionFactory;\n\n    private String environment = SqlSessionFactoryBean.class.getSimpleName();\n\n    private boolean failFast;\n\n    private Interceptor[] plugins;\n\n    private TypeHandler<?>[] typeHandlers;\n\n    private String typeHandlersPackage;\n\n    @SuppressWarnings(\"rawtypes\")\n    private Class<? extends TypeHandler> defaultEnumTypeHandler;\n\n    private Class<?>[] typeAliases;\n\n    private String typeAliasesPackage;\n\n    private Class<?> typeAliasesSuperType;\n\n    private LanguageDriver[] scriptingLanguageDrivers;\n\n    private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;\n\n    // issue #19. No default provider.\n    private DatabaseIdProvider databaseIdProvider;\n\n    private Class<? extends VFS> vfs;\n\n    private Cache cache;\n\n    private ObjectFactory objectFactory;\n\n    private ObjectWrapperFactory objectWrapperFactory;\n\n    /**\n     * @since 3.5.13\n     */\n    @Getter\n    private ApplicationContext applicationContext;\n\n    @Setter\n    private GlobalConfig globalConfig;\n\n    /**\n     * Sets the ObjectFactory.\n     *\n     * @param objectFactory a custom ObjectFactory\n     * @since 1.1.2\n     */\n    public void setObjectFactory(ObjectFactory objectFactory) {\n        this.objectFactory = objectFactory;\n    }\n\n    /**\n     * Sets the ObjectWrapperFactory.\n     *\n     * @param objectWrapperFactory a specified ObjectWrapperFactory\n     * @since 1.1.2\n     */\n    public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {\n        this.objectWrapperFactory = objectWrapperFactory;\n    }\n\n    /**\n     * Gets the DatabaseIdProvider\n     *\n     * @return a specified DatabaseIdProvider\n     * @since 1.1.0\n     */\n    public DatabaseIdProvider getDatabaseIdProvider() {\n        return databaseIdProvider;\n    }\n\n    /**\n     * Sets the DatabaseIdProvider. As of version 1.2.2 this variable is not initialized by default.\n     *\n     * @param databaseIdProvider a DatabaseIdProvider\n     * @since 1.1.0\n     */\n    public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {\n        this.databaseIdProvider = databaseIdProvider;\n    }\n\n    /**\n     * Gets the VFS.\n     *\n     * @return a specified VFS\n     */\n    public Class<? extends VFS> getVfs() {\n        return this.vfs;\n    }\n\n    /**\n     * Sets the VFS.\n     *\n     * @param vfs a VFS\n     */\n    public void setVfs(Class<? extends VFS> vfs) {\n        this.vfs = vfs;\n    }\n\n    /**\n     * Gets the Cache.\n     *\n     * @return a specified Cache\n     */\n    public Cache getCache() {\n        return this.cache;\n    }\n\n    /**\n     * Sets the Cache.\n     *\n     * @param cache a Cache\n     */\n    public void setCache(Cache cache) {\n        this.cache = cache;\n    }\n\n    /**\n     * Mybatis plugin list.\n     *\n     * @param plugins list of plugins\n     * @since 1.0.1\n     */\n    public void setPlugins(Interceptor... plugins) {\n        this.plugins = plugins;\n    }\n\n    /**\n     * Packages to search for type aliases.\n     *\n     * <p>\n     * Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.model}.\n     *\n     * @param typeAliasesPackage package to scan for domain objects\n     * @since 1.0.1\n     */\n    public void setTypeAliasesPackage(String typeAliasesPackage) {\n        this.typeAliasesPackage = typeAliasesPackage;\n    }\n\n    /**\n     * Super class which domain objects have to extend to have a type alias created. No effect if there is no package to\n     * scan configured.\n     *\n     * @param typeAliasesSuperType super class for domain objects\n     * @since 1.1.2\n     */\n    public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {\n        this.typeAliasesSuperType = typeAliasesSuperType;\n    }\n\n    /**\n     * Packages to search for type handlers.\n     *\n     * <p>\n     * Since 2.0.1, allow to specify a wildcard such as {@code com.example.*.typehandler}.\n     *\n     * @param typeHandlersPackage package to scan for type handlers\n     * @since 1.0.1\n     */\n    public void setTypeHandlersPackage(String typeHandlersPackage) {\n        this.typeHandlersPackage = typeHandlersPackage;\n    }\n\n\n\n    /**\n     * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes}\n     *\n     * @param typeHandlers Type handler list\n     * @since 1.0.1\n     */\n    public void setTypeHandlers(TypeHandler<?>... typeHandlers) {\n        this.typeHandlers = typeHandlers;\n    }\n    /**\n     * Set the default type handler class for enum.\n     *\n     * @param defaultEnumTypeHandler The default type handler class for enum\n     * @since 2.0.5\n     */\n    public void setDefaultEnumTypeHandler(\n        @SuppressWarnings(\"rawtypes\") Class<? extends TypeHandler> defaultEnumTypeHandler) {\n        this.defaultEnumTypeHandler = defaultEnumTypeHandler;\n    }\n    /**\n     * List of type aliases to register. They can be annotated with {@code Alias}\n     *\n     * @param typeAliases Type aliases list\n     * @since 1.0.1\n     */\n    public void setTypeAliases(Class<?>... typeAliases) {\n        this.typeAliases = typeAliases;\n    }\n\n    /**\n     * If true, a final check is done on Configuration to assure that all mapped statements are fully loaded and there is\n     * no one still pending to resolve includes. Defaults to false.\n     *\n     * @param failFast enable failFast\n     * @since 1.0.1\n     */\n    public void setFailFast(boolean failFast) {\n        this.failFast = failFast;\n    }\n\n    /**\n     * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is\n     * \"WEB-INF/mybatis-configuration.xml\".\n     *\n     * @param configLocation a location the MyBatis config file\n     */\n    public void setConfigLocation(Resource configLocation) {\n        this.configLocation = configLocation;\n    }\n\n    /**\n     * Set a customized MyBatis configuration.\n     *\n     * @param configuration MyBatis configuration\n     * @since 1.3.0\n     */\n    public void setConfiguration(MybatisConfiguration configuration) {\n        this.configuration = configuration;\n    }\n\n    public MybatisConfiguration getConfiguration() {\n        return this.configuration;\n    }\n\n    /**\n     * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration\n     * at runtime.\n     * <p>\n     * This is an alternative to specifying \"&lt;sqlmapper&gt;\" entries in an MyBatis config file. This property being\n     * based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.\n     * \"classpath*:sqlmap/*-mapper.xml\".\n     *\n     * @param mapperLocations location of MyBatis mapper files\n     */\n    public void setMapperLocations(Resource... mapperLocations) {\n        this.mapperLocations = mapperLocations;\n    }\n\n    /**\n     * Set optional properties to be passed into the SqlSession configuration, as alternative to a\n     * {@code &lt;properties&gt;} tag in the configuration xml file. This will be used to resolve placeholders in the\n     * config file.\n     *\n     * @param sqlSessionFactoryProperties optional properties for the SqlSessionFactory\n     */\n    public void setConfigurationProperties(Properties sqlSessionFactoryProperties) {\n        this.configurationProperties = sqlSessionFactoryProperties;\n    }\n\n    /**\n     * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} should\n     * match the one used by the {@code SqlSessionFactory}: for example, you could specify the same JNDI DataSource for\n     * both.\n     * <p>\n     * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code accessing\n     * this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.\n     * <p>\n     * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not a\n     * {@code TransactionAwareDataSourceProxy}. Only data access code may work with\n     * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the underlying target\n     * {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in, it will be\n     * unwrapped to extract its target {@code DataSource}.\n     *\n     * @param dataSource a JDBC {@code DataSource}\n     */\n    public void setDataSource(DataSource dataSource) {\n        if (dataSource instanceof TransactionAwareDataSourceProxy) {\n            // If we got a TransactionAwareDataSourceProxy, we need to perform\n            // transactions for its underlying target DataSource, else data\n            // access code won't see properly exposed transactions (i.e.\n            // transactions for the target DataSource).\n            this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();\n        } else {\n            this.dataSource = dataSource;\n        }\n    }\n\n    /**\n     * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}.\n     * <p>\n     * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By default,\n     * {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances.\n     *\n     * @param sqlSessionFactoryBuilder\n     *          a SqlSessionFactoryBuilder\n     */\n    public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {\n        this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;\n    }\n\n    /**\n     * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory}.\n     * <p>\n     * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: be it Spring transaction\n     * management, EJB CMT or plain JTA. If there is no active transaction, SqlSession operations will execute SQL\n     * statements non-transactionally.\n     * <p>\n     * <b>It is strongly recommended to use the default {@code TransactionFactory}.</b> If not used, any attempt at\n     * getting an SqlSession through Spring's MyBatis framework will throw an exception if a transaction is active.\n     *\n     * @see SpringManagedTransactionFactory\n     *\n     * @param transactionFactory\n     *          the MyBatis TransactionFactory\n     */\n    public void setTransactionFactory(TransactionFactory transactionFactory) {\n        this.transactionFactory = transactionFactory;\n    }\n\n    /**\n     * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you have set in the MyBatis config file. This is\n     * used only as a placeholder name. The default value is {@code SqlSessionFactoryBean.class.getSimpleName()}.\n     *\n     * @param environment\n     *          the environment name\n     */\n    public void setEnvironment(String environment) {\n        this.environment = environment;\n    }\n    /**\n     * Set scripting language drivers.\n     *\n     * @param scriptingLanguageDrivers scripting language drivers\n     * @since 2.0.2\n     */\n    public void setScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {\n        this.scriptingLanguageDrivers = scriptingLanguageDrivers;\n    }\n\n    /**\n     * Set a default scripting language driver class.\n     *\n     * @param defaultScriptingLanguageDriver A default scripting language driver class\n     * @since 2.0.2\n     */\n    public void setDefaultScriptingLanguageDriver(Class<? extends LanguageDriver> defaultScriptingLanguageDriver) {\n        this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver;\n    }\n\n    /**\n     * Add locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} configuration\n     * at runtime.\n     * <p>\n     * This is an alternative to specifying \"&lt;sqlmapper&gt;\" entries in an MyBatis config file. This property being\n     * based on Spring's resource abstraction also allows for specifying resource patterns here: e.g.\n     * \"classpath*:sqlmap/*-mapper.xml\".\n     *\n     * @param mapperLocations\n     *          location of MyBatis mapper files\n     *\n     * @see #setMapperLocations(Resource...)\n     *\n     * @since 3.0.2\n     */\n    public void addMapperLocations(Resource... mapperLocations) {\n        setMapperLocations(appendArrays(this.mapperLocations, mapperLocations, Resource[]::new));\n    }\n\n    /**\n     * Add type handlers.\n     *\n     * @param typeHandlers\n     *          Type handler list\n     *\n     * @since 3.0.2\n     */\n    public void addTypeHandlers(TypeHandler<?>... typeHandlers) {\n        setTypeHandlers(appendArrays(this.typeHandlers, typeHandlers, TypeHandler[]::new));\n    }\n\n    /**\n     * Add scripting language drivers.\n     *\n     * @param scriptingLanguageDrivers\n     *          scripting language drivers\n     *\n     * @since 3.0.2\n     */\n    public void addScriptingLanguageDrivers(LanguageDriver... scriptingLanguageDrivers) {\n        setScriptingLanguageDrivers(\n            appendArrays(this.scriptingLanguageDrivers, scriptingLanguageDrivers, LanguageDriver[]::new));\n    }\n\n    /**\n     * Add Mybatis plugins.\n     *\n     * @param plugins\n     *          list of plugins\n     *\n     * @since 3.0.2\n     */\n    public void addPlugins(Interceptor... plugins) {\n        setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));\n    }\n\n    /**\n     * Add type aliases.\n     *\n     * @param typeAliases\n     *          Type aliases list\n     *\n     * @since 3.0.2\n     */\n    public void addTypeAliases(Class<?>... typeAliases) {\n        setTypeAliases(appendArrays(this.typeAliases, typeAliases, Class[]::new));\n    }\n\n    private <T> T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction<T[]> generator) {\n        if (oldArrays == null) {\n            return newArrays;\n        } else {\n            if (newArrays == null) {\n                return oldArrays;\n            } else {\n                List<T> newList = new ArrayList<>(Arrays.asList(oldArrays));\n                newList.addAll(Arrays.asList(newArrays));\n                return newList.toArray(generator.apply(0));\n            }\n        }\n    }\n\n    /**\n     * 设置上下文对象\n     * <p>注意: 手动构建的bean对象需要手动赋值,通过xml属性初始化的会自动赋值</p>\n     * @since 3.5.13\n     * @param applicationContext ApplicationContext\n     */\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) {\n        this.applicationContext = applicationContext;\n        if (CompatibleHelper.hasCompatibleSet()) {\n            CompatibleHelper.getCompatibleSet().setContext(applicationContext);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        notNull(dataSource, \"Property 'dataSource' is required\");\n        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),\n            \"Property 'configuration' and 'configLocation' can not specified with together\");\n        this.sqlSessionFactory = buildSqlSessionFactory();\n    }\n\n\n    /**\n     * Build a {@code SqlSessionFactory} instance.\n     * <p>\n     * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a\n     * {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a\n     * {@link Configuration} instance directly(without config file).\n     * </p>\n     *\n     * @return SqlSessionFactory\n     * @throws IOException if loading the config file failed\n     */\n    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {\n\n        final Configuration targetConfiguration;\n\n        MybatisXMLConfigBuilder xmlConfigBuilder = null;\n        if (this.configuration != null) {\n            targetConfiguration = this.configuration;\n            if (targetConfiguration.getVariables() == null) {\n                targetConfiguration.setVariables(this.configurationProperties);\n            } else if (this.configurationProperties != null) {\n                targetConfiguration.getVariables().putAll(this.configurationProperties);\n            }\n        } else if (this.configLocation != null) {\n            xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);\n            targetConfiguration = xmlConfigBuilder.getConfiguration();\n        } else {\n            LOGGER.debug(() -> \"Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration\");\n            targetConfiguration = new MybatisConfiguration();\n            Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);\n        }\n\n        this.globalConfig = Optional.ofNullable(this.globalConfig).orElseGet(GlobalConfigUtils::defaults);\n        this.globalConfig.setDbConfig(Optional.ofNullable(this.globalConfig.getDbConfig()).orElseGet(GlobalConfig.DbConfig::new));\n\n        GlobalConfigUtils.setGlobalConfig(targetConfiguration, this.globalConfig);\n\n        Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);\n        Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);\n        Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);\n\n        if (hasLength(this.typeAliasesPackage)) {\n            scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()\n                .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())\n                .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);\n        }\n\n        if (!isEmpty(this.typeAliases)) {\n            Stream.of(this.typeAliases).forEach(typeAlias -> {\n                targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);\n                LOGGER.debug(() -> \"Registered type alias: '\" + typeAlias + \"'\");\n            });\n        }\n\n        if (!isEmpty(this.plugins)) {\n            Stream.of(this.plugins).forEach(plugin -> {\n                targetConfiguration.addInterceptor(plugin);\n                LOGGER.debug(() -> \"Registered plugin: '\" + plugin + \"'\");\n            });\n        }\n\n        if (hasLength(this.typeHandlersPackage)) {\n            scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())\n                .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))\n                .forEach(targetConfiguration.getTypeHandlerRegistry()::register);\n        }\n\n        if (!isEmpty(this.typeHandlers)) {\n            Stream.of(this.typeHandlers).forEach(typeHandler -> {\n                targetConfiguration.getTypeHandlerRegistry().register(typeHandler);\n                LOGGER.debug(() -> \"Registered type handler: '\" + typeHandler + \"'\");\n            });\n        }\n\n        targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);\n\n        if (!isEmpty(this.scriptingLanguageDrivers)) {\n            Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {\n                targetConfiguration.getLanguageRegistry().register(languageDriver);\n                LOGGER.debug(() -> \"Registered scripting language driver: '\" + languageDriver + \"'\");\n            });\n        }\n        Optional.ofNullable(this.defaultScriptingLanguageDriver)\n            .ifPresent(targetConfiguration::setDefaultScriptingLanguage);\n\n        if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls\n            try {\n                targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));\n            } catch (SQLException e) {\n                throw new IOException(\"Failed getting a databaseId\", e);\n            }\n        }\n\n        Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);\n\n        if (xmlConfigBuilder != null) {\n            try {\n                xmlConfigBuilder.parse();\n                LOGGER.debug(() -> \"Parsed configuration file: '\" + this.configLocation + \"'\");\n            } catch (Exception ex) {\n                throw new IOException(\"Failed to parse config resource: \" + this.configLocation, ex);\n            } finally {\n                ErrorContext.instance().reset();\n            }\n        }\n\n        targetConfiguration.setEnvironment(new Environment(this.environment,\n            this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,\n            this.dataSource));\n\n        if (this.mapperLocations != null) {\n            if (this.mapperLocations.length == 0) {\n                LOGGER.warn(() -> \"Property 'mapperLocations' was specified but matching resources are not found.\");\n            } else {\n                for (Resource mapperLocation : this.mapperLocations) {\n                    if (mapperLocation == null) {\n                        continue;\n                    }\n                    try {\n                        MybatisXMLMapperBuilder xmlMapperBuilder = new MybatisXMLMapperBuilder(mapperLocation.getInputStream(),\n                            targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());\n                        xmlMapperBuilder.parse();\n                    } catch (Exception e) {\n                        throw new IOException(\"Failed to parse mapping resource: '\" + mapperLocation + \"'\", e);\n                    } finally {\n                        ErrorContext.instance().reset();\n                    }\n                    LOGGER.debug(() -> \"Parsed mapper file: '\" + mapperLocation + \"'\");\n                }\n            }\n        } else {\n            LOGGER.debug(() -> \"Property 'mapperLocations' was not specified.\");\n        }\n\n        final SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(targetConfiguration);\n\n        SqlHelper.FACTORY = sqlSessionFactory;\n\n        if (globalConfig.isBanner()) {\n            System.out.println(\" _ _   |_  _ _|_. ___ _ |    _ \");\n            System.out.println(\"| | |\\\\/|_)(_| | |_\\\\  |_)||_|_\\\\ \");\n            System.out.println(\"     /               |         \");\n            System.out.println(\"                        \" + MybatisPlusVersion.getVersion() + \" \");\n        }\n\n        return sqlSessionFactory;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public SqlSessionFactory getObject() throws Exception {\n        if (this.sqlSessionFactory == null) {\n            afterPropertiesSet();\n        }\n\n        return this.sqlSessionFactory;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public Class<? extends SqlSessionFactory> getObjectType() {\n        return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public boolean isSingleton() {\n        return true;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onApplicationEvent(ContextRefreshedEvent event) {\n        if (failFast) {\n            // fail-fast -> check all statements are completed\n            this.sqlSessionFactory.getConfiguration().getMappedStatementNames();\n        }\n    }\n\n    private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {\n        Set<Class<?>> classes = new HashSet<>();\n        String[] packagePatternArray = tokenizeToStringArray(packagePatterns,\n            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);\n        for (String packagePattern : packagePatternArray) {\n            Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX\n                + ClassUtils.convertClassNameToResourcePath(packagePattern) + \"/**/*.class\");\n            for (Resource resource : resources) {\n                try {\n                    ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();\n                    Class<?> clazz = Resources.classForName(classMetadata.getClassName());\n                    if (assignableType == null || assignableType.isAssignableFrom(clazz)) {\n                        classes.add(clazz);\n                    }\n                } catch (Throwable e) {\n                    LOGGER.warn(() -> \"Cannot load the '\" + resource + \"'. Cause by \" + e.toString());\n                }\n            }\n        }\n        return classes;\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SqlRunner.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.extension.toolkit;\n\nimport com.baomidou.mybatisplus.core.assist.AbstractSqlRunner;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport org.apache.ibatis.builder.MapperBuilderAssistant;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionUtils;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * SqlRunner 执行 SQL\n * <p>\n * 自3.5.12开始,(当传入的参数是单参数时,支持使用Map,Array,List,JavaBean)\n * <li>当参数为 Map 时可通过{key}进行属性访问\n * <li>当参数为 JavaBean 时可通过{property}进行属性访问\n * <li>当参数为 List 时直接访问索引 {0} </li>\n * <li>当参数为 Array 时直接访问索引 {0} </li>\n * </p>\n *\n * @author Caratacus, nieqiurong\n * @since 2016-12-11\n */\npublic class SqlRunner extends AbstractSqlRunner {\n\n    /**\n     * 日志对象\n     */\n    private static final Log LOG = LogFactory.getLog(SqlRunner.class);\n\n    /**\n     * 默认实例 (使用{@link SqlHelper#FACTORY}进行会话操作)\n     */\n    public static final SqlRunner DEFAULT = new SqlRunner();\n\n    /**\n     * 实体类 (需要被扫描注入的实体对象,当未指定时,将使用{@link SqlHelper#FACTORY}进行会话操作)\n     *\n     * @see com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(MapperBuilderAssistant, Class)\n     */\n    private Class<?> clazz;\n\n    /**\n     * 默认构造,使用{@link SqlHelper#FACTORY}进行会话操作\n     */\n    public SqlRunner() {\n    }\n\n    /**\n     * 通过实体类构造\n     *\n     * @param clazz 实体类\n     */\n    public SqlRunner(Class<?> clazz) {\n        this.clazz = clazz;\n    }\n\n    /**\n     * 获取默认的SqlQuery(适用于单库)\n     *\n     * @return this\n     */\n    public static SqlRunner db() {\n        return DEFAULT;\n    }\n\n    /**\n     * 根据当前class对象获取SqlQuery(适用于多库)\n     *\n     * @param clazz 实体类\n     * @return this\n     */\n    public static SqlRunner db(Class<?> clazz) {\n        return new SqlRunner(clazz);\n    }\n\n    /**\n     * 执行插入语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 插入结果\n     */\n    @Override\n    @Transactional\n    public boolean insert(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.insert(INSERT, sqlMap(sql, args)));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 执行删除语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 删除结果\n     */\n    @Override\n    @Transactional\n    public boolean delete(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.delete(DELETE, sqlMap(sql, args)));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 执行更新语句\n     *\n     * @param sql  指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 更新结果\n     */\n    @Override\n    @Transactional\n    public boolean update(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retBool(sqlSession.update(UPDATE, sqlMap(sql, args)));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据sql查询Map结果集\n     * <p>SqlRunner.db().selectList(\"select * from tbl_user where name={0}\", \"Caratacus\")</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数列表\n     * @return 结果集\n     */\n    @Override\n    public List<Map<String, Object>> selectList(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return sqlSession.selectList(SELECT_LIST, sqlMap(sql, args));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据sql查询一个字段值的结果集\n     * <p>注意：该方法只会返回一个字段的值， 如果需要多字段，请参考{@link #selectList(String, Object...)}</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 结果集\n     */\n    @Override\n    public List<Object> selectObjs(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return sqlSession.selectList(SELECT_OBJS, sqlMap(sql, args));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 根据sql查询一个字段值的一条结果\n     * <p>注意：该方法只会返回一个字段的值， 如果需要多字段，请参考{@link #selectOne(String, Object...)}</p>\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 结果\n     */\n    @Override\n    public Object selectObj(String sql, Object... args) {\n        return SqlHelper.getObject(LOG, selectObjs(sql, args));\n    }\n\n    /**\n     * 查询总数\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 总记录数\n     */\n    @Override\n    public long selectCount(String sql, Object... args) {\n        SqlSession sqlSession = sqlSession();\n        try {\n            return SqlHelper.retCount(sqlSession.<Long>selectOne(COUNT, sqlMap(sql, args)));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n    }\n\n    /**\n     * 获取单条记录\n     *\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @return 单行结果集 (当执行语句返回多条记录时,只会选取第一条记录)\n     */\n    @Override\n    public Map<String, Object> selectOne(String sql, Object... args) {\n        return SqlHelper.getObject(LOG, selectList(sql, args));\n    }\n\n    /**\n     * 分页查询\n     *\n     * @param page 分页对象\n     * @param sql  sql语句，可添加参数，指定参数的格式: {0}, {1} 或者 {property1}, {property2}\n     * @param args 参数\n     * @param <E>  E\n     * @return 分页数据\n     */\n    @Override\n    public <E extends IPage<Map<String, Object>>> E selectPage(E page, String sql, Object... args) {\n        if (null == page) {\n            return null;\n        }\n        SqlSession sqlSession = sqlSession();\n        try {\n            page.setRecords(sqlSession.selectList(SELECT_LIST, sqlMap(sql, page, args)));\n        } finally {\n            closeSqlSession(sqlSession);\n        }\n        return page;\n    }\n\n    /**\n     * 获取Session 默认自动提交\n     */\n    private SqlSession sqlSession() {\n        return SqlSessionUtils.getSqlSession(getSqlSessionFactory());\n    }\n\n    /**\n     * 释放sqlSession\n     *\n     * @param sqlSession session\n     */\n    private void closeSqlSession(SqlSession sqlSession) {\n        SqlSessionUtils.closeSqlSession(sqlSession, getSqlSessionFactory());\n    }\n\n    /**\n     * 获取SqlSessionFactory\n     */\n    private SqlSessionFactory getSqlSessionFactory() {\n        return Optional.ofNullable(clazz).map(GlobalConfigUtils::currentSessionFactory).orElse(SqlHelper.FACTORY);\n    }\n\n    /**\n     * @deprecated 3.5.3.2\n     */\n    @Deprecated\n    public void close() {\n\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/resources/META-INF/services/com.baomidou.mybatisplus.core.spi.CompatibleSet",
    "content": "com.baomidou.mybatisplus.extension.spi.SpringCompatibleSet\n"
  },
  {
    "path": "mybatis-plus-spring/src/main/resources/META-INF/services/com.baomidou.mybatisplus.extension.spi.CompatibleSet",
    "content": "com.baomidou.mybatisplus.extension.spi.SpringCompatibleSet\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/java/com/baomidou/mybatisplus/test/service/ServiceTest.java",
    "content": "package com.baomidou.mybatisplus.test.service;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.override.MybatisMapperProxyFactory;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\n\nimport java.util.Objects;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author nieqiurong 2021/1/19.\n */\npublic class ServiceTest {\n\n    static class Demo {\n\n    }\n\n    interface DemoMapper extends BaseMapper<Demo> {\n\n    }\n\n    static class DemoServiceImpl extends ServiceImpl<DemoMapper, Demo> {\n\n        public DemoServiceImpl(BaseMapper<Demo> baseMapper) {\n            super.baseMapper = (DemoMapper) baseMapper;\n        }\n    }\n\n    static class DemoServiceExtend extends DemoServiceImpl {\n\n        public DemoServiceExtend(BaseMapper<Demo> baseMapper) {\n            super(baseMapper);\n        }\n    }\n\n    @Test\n    @SuppressWarnings(\"unchecked\")\n    void genericTest() {\n        MybatisMapperProxyFactory<? extends BaseMapper<?>> mybatisMapperProxyFactory = new MybatisMapperProxyFactory<>(DemoMapper.class);\n        BaseMapper<Demo> baseMapper = (BaseMapper<Demo>) mybatisMapperProxyFactory.newInstance(Mockito.mock(SqlSession.class));\n        IService<Demo>[] services = new IService[]{new DemoServiceImpl(baseMapper), new DemoServiceExtend(baseMapper)};\n        for (IService<Demo> service : services) {\n            ServiceImpl<?,?> impl = (ServiceImpl<?,?>) service;\n            Assertions.assertEquals(Demo.class, impl.getEntityClass());\n            Assertions.assertEquals(DemoMapper.class, impl.getMapperClass());\n        }\n    }\n\n\n    static class MyServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {\n\n    }\n\n    static class MyServiceExtend extends MyServiceImpl<DemoMapper, Demo> {\n\n    }\n\n    @Test\n    void testSuperClassGenericType() {\n        // 多重继承测试\n        assertThat(Objects.equals(ReflectionKit.getSuperClassGenericType(MyServiceExtend.class,\n            ServiceImpl.class, 0), DemoMapper.class));\n        assertThat(Objects.equals(ReflectionKit.getSuperClassGenericType(MyServiceExtend.class,\n            ServiceImpl.class, 1), Demo.class));\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/instance.kt",
    "content": "package com.baomidou.mybatisplus.test\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper\nimport org.apache.ibatis.builder.MapperBuilderAssistant\nimport kotlin.reflect.KClass\n\n/**\n * @author hcl\n * Create at 2019/12/25\n */\nobject TestMybatisConfiguration : MybatisConfiguration()\n\n/**\n * 初始化表信息\n */\nfun initTableInfo(kClass: KClass<*>) {\n    TableInfoHelper.initTableInfo(MapperBuilderAssistant(TestMybatisConfiguration, \"\"), kClass.java)\n}\n\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/BaseDbTest.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration\nimport com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder\nimport com.baomidou.mybatisplus.core.MybatisXMLMapperBuilder\nimport com.baomidou.mybatisplus.core.config.GlobalConfig\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils\nimport com.baomidou.mybatisplus.core.toolkit.ExceptionUtils\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils\nimport com.baomidou.mybatisplus.extension.toolkit.SqlRunner\nimport org.apache.ibatis.io.Resources\nimport org.apache.ibatis.logging.slf4j.Slf4jImpl\nimport org.apache.ibatis.mapping.Environment\nimport org.apache.ibatis.plugin.Interceptor\nimport org.apache.ibatis.session.Configuration\nimport org.apache.ibatis.session.ExecutorType\nimport org.apache.ibatis.session.SqlSession\nimport org.apache.ibatis.session.SqlSessionFactory\nimport org.apache.ibatis.transaction.managed.ManagedTransactionFactory\nimport org.apache.ibatis.type.TypeReference\nimport org.h2.Driver\nimport org.springframework.jdbc.core.JdbcTemplate\nimport org.springframework.jdbc.datasource.SimpleDriverDataSource\nimport java.io.IOException\nimport java.util.function.Consumer\nimport javax.sql.DataSource\n\n/**\n * @author miemie\n * @since 2020-06-23\n */\nabstract class BaseDbTest<T> : TypeReference<T>() {\n    protected var sqlSessionFactory: SqlSessionFactory\n    protected var mapper: Class<T>\n    protected var jdbcTemplate: JdbcTemplate\n\n    init {\n        val ds = dataSource()\n        val tableSql = tableSql()\n        val tableDataSql = tableDataSql()\n        val mapperXml = mapperXml()\n        val globalConfig = globalConfig()\n        val interceptors = interceptors()\n        val consumer = consumer()\n        mapper = rawType as Class<T>\n        jdbcTemplate = JdbcTemplate(ds)\n        if (CollectionUtils.isNotEmpty(tableSql)) {\n            for (sql in tableSql!!) {\n                if (StringUtils.isNotBlank(sql)) {\n                    jdbcTemplate.execute(sql)\n                }\n            }\n        }\n        if (StringUtils.isNotBlank(tableDataSql)) {\n            jdbcTemplate.execute(tableDataSql)\n        }\n        val builder = MybatisSqlSessionFactoryBuilder()\n        val environment = Environment(\"test\", ManagedTransactionFactory(), ds)\n        val configuration = MybatisConfiguration(environment)\n        consumer?.accept(configuration)\n        GlobalConfigUtils.setGlobalConfig(configuration, globalConfig)\n        configuration.logImpl = Slf4jImpl::class.java\n        if (StringUtils.isNotBlank(mapperXml)) {\n            try {\n                val inputStream = Resources.getResourceAsStream(mapperXml)\n                val xmlMapperBuilder = MybatisXMLMapperBuilder(\n                    inputStream,\n                    configuration, mapperXml, configuration.sqlFragments\n                )\n                xmlMapperBuilder.parse()\n            } catch (e: IOException) {\n                throw ExceptionUtils.mpe(e)\n            }\n        }\n        configuration.addMapper(mapper)\n        otherMapper().forEach(Consumer { type: Class<*>? -> configuration.addMapper(type) })\n        if (CollectionUtils.isNotEmpty(interceptors)) {\n            interceptors!!.forEach(Consumer { interceptor: Interceptor? -> configuration.addInterceptor(interceptor) })\n        }\n        sqlSessionFactory = builder.build(configuration)\n    }\n\n    private fun dataSource(): DataSource {\n        val dataSource = SimpleDriverDataSource()\n        dataSource.driver = Driver()\n        dataSource.url = \"jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\"\n        dataSource.username = \"sa\"\n        dataSource.password = \"\"\n        return dataSource\n    }\n\n    protected fun sqlSession(type: ExecutorType?): SqlSession {\n        return sqlSessionFactory.openSession(type)\n    }\n\n    protected fun doTest(consumer: Consumer<T>) {\n        sqlSession(null).use { sqlSession -> doTest(sqlSession, consumer) }\n    }\n\n    protected fun doTestAutoCommit(consumer: Consumer<T>) {\n        sqlSession(null).use { sqlSession -> doTestAutoCommit(sqlSession, consumer) }\n    }\n\n    protected fun doTest(sqlSession: SqlSession, consumer: Consumer<T>) {\n        doMapper(sqlSession, false, consumer)\n    }\n\n    protected fun doTestAutoCommit(sqlSession: SqlSession, consumer: Consumer<T>) {\n        doMapper(sqlSession, true, consumer)\n    }\n\n    protected fun doMapper(sqlSession: SqlSession, commit: Boolean, consumer: Consumer<T>) {\n        val t = sqlSession.getMapper(mapper)\n        consumer.accept(t)\n        if (commit) {\n            sqlSession.commit()\n        } else {\n            sqlSession.rollback()\n        }\n    }\n\n    protected open fun tableSql(): List<String?>? {\n        return null\n    }\n\n    protected open fun tableDataSql(): String? {\n        return null\n    }\n\n    protected fun mapperXml(): String? {\n        return null\n    }\n\n    protected fun interceptors(): List<Interceptor?>? {\n        return null\n    }\n\n    protected fun globalConfig(): GlobalConfig {\n        return GlobalConfigUtils.defaults()\n    }\n\n    protected fun consumer(): Consumer<Configuration>? {\n        return null\n    }\n\n    protected fun otherMapper(): List<Class<*>> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/ChainWrappersTest.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.core.metadata.IPage\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page\nimport com.baomidou.mybatisplus.extension.toolkit.ChainWrappers\nimport com.baomidou.mybatisplus.extension.toolkit.Db\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.Test\nimport java.util.*\n\nclass ChainWrappersTest : BaseDbTest<UserMapper>() {\n\n    @Test\n    fun testQueryChain() {\n        val list = ChainWrappers.ktQueryChain(User::class.java).eq(User::id, 1).list()\n        Assertions.assertEquals(1, list.size)\n        val listRid = ChainWrappers.ktQueryChain(User::class.java).eq(User::roleId, 1).list()\n        Assertions.assertEquals(2, listRid.size)\n        val oneUser = ChainWrappers.ktQueryChain(User::class.java).eq(User::id, 1).one()\n        Assertions.assertEquals(\"gozei\", oneUser.name)\n        val count = ChainWrappers.ktQueryChain(User::class.java).eq(User::id, 1).count()\n        Assertions.assertEquals(1, count)\n        val exists = ChainWrappers.ktQueryChain(User::class.java).eq(User::id, 1).exists()\n        Assertions.assertEquals(true, exists)\n        val oneIdName = ChainWrappers.ktQueryChain(User::class.java).eq(User::id, 1).eq(User::name, \"gozei\").one()\n        Assertions.assertEquals(1, oneIdName.roleId)\n        val page: IPage<User> = Db.page(Page(1, 3), User::class.java)\n        val pageU = ChainWrappers.ktQueryChain(User::class.java).page(page)\n        Assertions.assertEquals(3, pageU.size)\n    }\n\n    @Test\n    fun testUpdate() {\n        ChainWrappers.ktUpdateChain(User::class.java).eq(User::id, 3).set(User::name, \"haku\").update()\n        Assertions.assertEquals(\"haku\", Db.ktQuery(User::class.java).eq(User::id, 3).one().name)\n        ChainWrappers.ktUpdateChain(User::class.java).eq(User::id, 2).set(User::name, \"haku\").set(User::roleId, 4)\n            .update()\n        Assertions.assertEquals(4, Db.ktQuery(User::class.java).eq(User::id, 2).one().roleId)\n    }\n\n    @Test\n    fun testDefaultMethod() {\n        doTestAutoCommit(fun(m) {\n            Assertions.assertEquals(\"hello baomidou!\", m.hello())\n            Assertions.assertNotNull(m.findById(1))\n            Assertions.assertNull(m.findById(-1))\n        })\n    }\n\n    @Test\n    fun testSetSql() {\n        Assertions.assertTrue(\n            ChainWrappers.ktUpdateChain(User::class.java).eq(User::id, 3).setSql(\"username = {0}\", \"haku\").update()\n        );\n    }\n\n    @Test\n    fun testSelectByPredicate() {\n        Assertions.assertDoesNotThrow { ChainWrappers.ktQueryChain(User::class.java).select({ true }).list() }\n        doTestAutoCommit(fun(m) {\n            Assertions.assertDoesNotThrow {\n                m.selectList(KtQueryWrapper(User()).select { true })\n            }\n        })\n    }\n\n    override fun tableDataSql(): String {\n        return \"insert into `sys_user`(id,username,role_id) values(1,'gozei',1),(2,'chocolate',2),(3,'sheep',1)\"\n    }\n\n    override fun tableSql(): List<String>? {\n        return Arrays.asList(\n            \"drop table if exists `sys_user`\", \"CREATE TABLE IF NOT EXISTS `sys_user` (\" +\n                \"id INT NOT NULL,\" +\n                \"username VARCHAR(30) NULL DEFAULT NULL,\" +\n                \"role_id INT NULL DEFAULT NULL,\" +\n                \"PRIMARY KEY (id))\"\n        )\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/DbTest.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.extension.toolkit.Db\nimport org.apache.ibatis.exceptions.PersistenceException\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.Test\nimport java.util.*\n\n\nclass DbTest : BaseDbTest<UserMapper>() {\n\n    @Test\n    fun testQuery() {\n        val list = Db.ktQuery(User::class.java).eq(User::id, 1).list()\n        Assertions.assertEquals(1, list.size)\n        val user1 = Db.ktQuery(User::class.java).eq(User::id, 1).one()\n        Assertions.assertEquals(\"gozei\", user1.name)\n        val user2 = Db.ktQuery(User::class.java).eq(User::name, \"gozei\").eq(User::id, 1).one()\n        Assertions.assertEquals(1, user2::id.get())\n        val user3 = Db.ktQuery(User::class.java).list().first()\n        Assertions.assertEquals(\"gozei\", user3.name)\n        val exists = Db.ktQuery(User::class.java).eq(User::id, 1).exists()\n        Assertions.assertTrue(exists)\n    }\n\n    @Test\n    fun testUpdate() {\n        Db.ktUpdate(User::class.java).eq(User::id, 1).set(User::name, \"sheep\").update()\n        Assertions.assertEquals(\"sheep\", Db.ktQuery(User::class.java).eq(User::id, 1).one().name)\n        Db.ktUpdate(User::class.java).eq(User::name, \"gozei\").set(User::name, \"sheep\").update()\n        Assertions.assertEquals(\"sheep\", Db.ktQuery(User::class.java).eq(User::id, 1).one().name)\n    }\n\n    @Test\n    fun exceptionTest() {\n        assertThrows(PersistenceException::class.java) {\n            Db.ktQuery(User::class.java).eq(null, 1).one()\n        }\n    }\n\n    @Test\n    fun remove() {\n        val isSuccessId = Db.remove(Db.ktQuery(User::class.java).eq(User::id, 1).wrapper)\n        Assertions.assertTrue(isSuccessId)\n        val isSuccessName = Db.remove(Db.ktQuery(User::class.java).eq(User::name, \"gozei\").wrapper)\n        Assertions.assertFalse(isSuccessName)\n    }\n\n    override fun tableDataSql(): String {\n        return \"insert into `sys_user`(id,username,role_id) values(1,'gozei',null),(2,'chocolate',null)\"\n    }\n\n    override fun tableSql(): List<String>? {\n        return Arrays.asList(\n            \"drop table if exists `sys_user`\", \"CREATE TABLE IF NOT EXISTS `sys_user` (\" +\n                \"id INT NOT NULL,\" +\n                \"username VARCHAR(30) NULL DEFAULT NULL,\" +\n                \"role_id INT NULL DEFAULT NULL,\" +\n                \"PRIMARY KEY (id))\"\n        )\n    }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/FixIssue1986.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper\nimport com.baomidou.mybatisplus.test.initTableInfo\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n/**\n * 修复：https://github.com/baomidou/mybatis-plus/issues/1986\n *\n * @author hcl\n * Create at 2019/12/24\n */\nclass FixIssue1986 {\n\n    @BeforeEach\n    fun setUp() {\n        initTableInfo(CustomerEntity::class)\n    }\n\n    /**\n     * 测试补全代码\n     */\n    @Test\n    fun test1986() {\n        val wrapper = fillQueryWrapper(OpportunityWebPageQuery())\n        print(wrapper.targetSql)\n        // (valid = ? AND district_id = ? AND name LIKE ? OR phone LIKE ? AND (valid = ? AND district_id = ?))\n    }\n\n}\n\n/**\n * 用户代码\n */\nprivate fun fillQueryWrapper(query: OpportunityWebPageQuery): KtQueryWrapper<CustomerEntity> {\n    return KtQueryWrapper(CustomerEntity::class.java)\n        .eq(CustomerEntity::valid, query.valid)\n        .eq(!query.districtId.isNullOrEmpty(), CustomerEntity::districtId, query.districtId)\n        .eq(!query.cityId.isNullOrEmpty(), CustomerEntity::cityId, query.cityId)\n        .eq(!query.provinceId.isNullOrEmpty(), CustomerEntity::provinceId, query.provinceId)\n        .`in`(!query.region.isNullOrEmpty() && RegionType.of(query.region!!.toInt())?.areaCodes?.toList()?.isNullOrEmpty() != false,\n            CustomerEntity::provinceId, RegionType.of(query.region!!.toInt())?.areaCodes?.toList())\n        .and(!query.searchKey.isNullOrEmpty()) { i ->\n            i.like(CustomerEntity::name, query.searchKey).or()\n                .like(CustomerEntity::phone, query.searchKey)\n        }\n        .eq(query.opportunityType != 0, CustomerEntity::type, query.opportunityType)\n}\n\n// 用户代码模拟补全\nclass CustomerEntity(\n    var valid: String? = null,\n    var name: String? = null,\n    var phone: String? = null,\n    var provinceId: String? = null,\n    var districtId: String? = null,\n    var cityId: String? = null,\n    var type: Int = 0\n)\n\nclass OpportunityWebPageQuery(\n    var valid: String = \"123\",\n    var searchKey: String? = \"123\",\n    var provinceId: String? = \"123\",\n    var districtId: String? = \"123\",\n    var cityId: String? = \"123\",\n    var region: String? = \"123\",\n    var opportunityType: Int = 0\n)\n\nobject RegionType {\n    fun of(toInt: Int): Area? = Area(toInt)\n}\n\nclass Area(i: Int) {\n    var areaCodes: IntArray? = IntArray(i) { it }\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/User.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.annotation.TableField\nimport com.baomidou.mybatisplus.annotation.TableName\n\n@TableName(\"sys_user\")\nclass User {\n\n    var id: Int? = null\n\n    @TableField(\"username\")\n    var name: String? = null\n\n    var roleId: Int? = null\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/UserMapper.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper\n\ninterface UserMapper : BaseMapper<User> {\n\n    fun hello(): String {\n        return \"hello baomidou!\";\n    }\n\n    fun findById(id: Int): User? {\n        return selectOne(KtQueryWrapper(User::class.java).eq(User::id, id));\n    }\n\n}\n"
  },
  {
    "path": "mybatis-plus-spring/src/test/kotlin/com/baomidou/mybatisplus/test/kotlin/WrapperTest.kt",
    "content": "package com.baomidou.mybatisplus.test.kotlin\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper\nimport com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper\nimport com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper\nimport org.apache.ibatis.builder.MapperBuilderAssistant\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass WrapperTest {\n\n    @BeforeEach\n    fun beforeInit() {\n        TableInfoHelper.initTableInfo(MapperBuilderAssistant(MybatisConfiguration(), \"\"), User::class.java)\n    }\n\n    @Test\n    fun testLambdaQuery() {\n        logSqlSegment(\"测试1.1 LambdaKt\", KtQueryWrapper(User()).eq(User::name, \"sss\").eq(User::roleId, \"sss2\")\n            .apply(\"a=1\").apply(\"b={0}\", 3).apply(\"c={0} and d={1}\", 4, 5))\n    }\n\n    @Test\n    fun testLambdaUpdate() {\n        logSqlSegmentAndSetSql(\"测试2.1 LambdaKt\", KtUpdateWrapper(User()).eq(User::name, \"sss\").eq(User::roleId, \"sss2\")\n            .setSql(\"a=1\").setSql(\"b={0}\", 3).setSql(\"c={0},d={1}\", 4, 5))\n    }\n\n    private fun logSqlSegment(explain: String, wp: KtQueryWrapper<*>) {\n        println(String.format(\" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓\", explain))\n        println(wp.sqlSegment)\n        wp.paramNameValuePairs.forEach(::println)\n    }\n\n    private fun logSqlSegmentAndSetSql(explain: String, wp: KtUpdateWrapper<*>) {\n        println(String.format(\" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓   ->(%s)<-   ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓\", explain))\n        println(wp.sqlSegment)\n        println(wp.sqlSet)\n        wp.paramNameValuePairs.forEach(::println)\n    }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "buildscript {\n    repositories {\n        maven { url \"https://maven.aliyun.com/repository/public\" }\n        maven { url \"https://maven.aliyun.com/repository/gradle-plugin\" }\n        maven { url \"https://plugins.gradle.org/m2/\" }\n        mavenCentral()\n    }\n\n    dependencies {\n        //noinspection DifferentKotlinGradleVersion\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0\"\n        classpath \"io.freefair.gradle:lombok-plugin:8.10\"\n        classpath \"tech.yanand.maven-central-publish:tech.yanand.maven-central-publish.gradle.plugin:1.1.0\"\n    }\n}\n\nrootProject.name = 'mybatis-plus-root'\ninclude 'mybatis-plus'\ninclude 'mybatis-plus-core'\ninclude 'mybatis-plus-annotation'\ninclude 'mybatis-plus-extension'\ninclude 'mybatis-plus-generator'\ninclude 'mybatis-plus-bom'\ninclude 'mybatis-plus-spring'\ninclude 'spring-boot-starter'\ninclude ':spring-boot-starter:mybatis-plus-boot-starter'\ninclude ':spring-boot-starter:mybatis-plus-boot-starter-test'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot-autoconfigure'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot-test-autoconfigure'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot3-starter'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot3-starter-test'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot4-starter'\ninclude ':spring-boot-starter:mybatis-plus-spring-boot4-starter-test'\ninclude 'mybatis-plus-jsqlparser-support'\ninclude ':mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser'\ninclude ':mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-common'\ninclude ':mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-4.9'\ninclude ':mybatis-plus-jsqlparser-support:mybatis-plus-jsqlparser-5.0'\n"
  },
  {
    "path": "spring-boot-starter/build.gradle",
    "content": "tasks.matching {it.group == 'publishing' || it.group == 'central publish' }.each { it.enabled = false }\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/build.gradle",
    "content": "dependencies {\n    api project(\":mybatis-plus\")\n    api \"${lib.\"mybatis-spring\"}\"\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBootVersion}\")\n    annotationProcessor \"org.springframework.boot:spring-boot-autoconfigure-processor:${springBootVersion}\"\n    annotationProcessor \"org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}\"\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-jdbc\"\n    implementation \"org.springframework.boot:spring-boot-configuration-processor\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure-processor\"\n    implementation \"${lib['mybatis-thymeleaf']}\"\n    implementation \"${lib.'mybatis-velocity'}\"\n    implementation \"${lib.'mybatis-freemarker'}\"\n    implementation \"${lib.'spring-cloud-commons'}\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test\"\n    testImplementation \"${lib.'mybatis-spring-boot-starter'}\"\n    testImplementation \"${lib.h2}\"\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.ISqlInjector;\nimport com.baomidou.mybatisplus.extension.spring.MybatisPlusApplicationContextAware;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.mapper.MapperFactoryBean;\nimport org.mybatis.spring.mapper.MapperScannerConfigurer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.beans.BeanWrapperImpl;\nimport org.springframework.beans.factory.*;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.boot.autoconfigure.AutoConfigurationPackages;\nimport org.springframework.boot.autoconfigure.AutoConfigureAfter;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.EnvironmentAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.env.Environment;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\nimport javax.sql.DataSource;\nimport java.beans.PropertyDescriptor;\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a\n * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.\n * <p>\n * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a\n * configuration file is specified as a property, those will be considered,\n * otherwise this auto-configuration will attempt to register mappers based on\n * the interface definitions in or under the root auto-configuration package.\n * </p>\n * <p> copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}</p>\n *\n * @author Eddú Meléndez\n * @author Josh Long\n * @author Kazuki Shimizu\n * @author Eduardo Macarrón\n */\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})\n@ConditionalOnSingleCandidate(DataSource.class)\n@EnableConfigurationProperties(MybatisPlusProperties.class)\n@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})\npublic class MybatisPlusAutoConfiguration implements InitializingBean {\n\n    private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);\n\n    private final MybatisPlusProperties properties;\n\n    private final Interceptor[] interceptors;\n\n    private final TypeHandler[] typeHandlers;\n\n    private final LanguageDriver[] languageDrivers;\n\n    private final ResourceLoader resourceLoader;\n\n    private final DatabaseIdProvider databaseIdProvider;\n\n    private final List<ConfigurationCustomizer> configurationCustomizers;\n\n    private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;\n\n    private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;\n\n    private final ApplicationContext applicationContext;\n\n    public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,\n                                        ObjectProvider<Interceptor[]> interceptorsProvider,\n                                        ObjectProvider<TypeHandler[]> typeHandlersProvider,\n                                        ObjectProvider<LanguageDriver[]> languageDriversProvider,\n                                        ResourceLoader resourceLoader,\n                                        ObjectProvider<DatabaseIdProvider> databaseIdProvider,\n                                        ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,\n                                        ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers,\n                                        ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,\n                                        ApplicationContext applicationContext) {\n        this.properties = properties;\n        this.interceptors = interceptorsProvider.getIfAvailable();\n        this.typeHandlers = typeHandlersProvider.getIfAvailable();\n        this.languageDrivers = languageDriversProvider.getIfAvailable();\n        this.resourceLoader = resourceLoader;\n        this.databaseIdProvider = databaseIdProvider.getIfAvailable();\n        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();\n        this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();\n        this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();\n        this.applicationContext = applicationContext;\n    }\n\n    @Override\n    public void afterPropertiesSet() {\n        if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) {\n            mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));\n        }\n        checkConfigFileExists();\n    }\n\n    private void checkConfigFileExists() {\n        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {\n            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());\n            Assert.state(resource.exists(),\n                \"Cannot find config location: \" + resource + \" (please add config file or check your Mybatis configuration)\");\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();\n        factory.setDataSource(dataSource);\n        factory.setVfs(SpringBootVFS.class);\n        factory.setApplicationContext(this.applicationContext);\n        if (StringUtils.hasText(this.properties.getConfigLocation())) {\n            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));\n        }\n        applyConfiguration(factory);\n        if (this.properties.getConfigurationProperties() != null) {\n            factory.setConfigurationProperties(this.properties.getConfigurationProperties());\n        }\n        if (!ObjectUtils.isEmpty(this.interceptors)) {\n            factory.setPlugins(this.interceptors);\n        }\n        if (this.databaseIdProvider != null) {\n            factory.setDatabaseIdProvider(this.databaseIdProvider);\n        }\n        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {\n            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());\n        }\n        if (this.properties.getTypeAliasesSuperType() != null) {\n            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());\n        }\n        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {\n            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());\n        }\n        if (!ObjectUtils.isEmpty(this.typeHandlers)) {\n            factory.setTypeHandlers(this.typeHandlers);\n        }\n        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {\n            factory.setMapperLocations(this.properties.resolveMapperLocations());\n        }\n        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);\n\n        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();\n        if (!ObjectUtils.isEmpty(this.languageDrivers)) {\n            factory.setScriptingLanguageDrivers(this.languageDrivers);\n        }\n        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);\n\n        applySqlSessionFactoryBeanCustomizers(factory);\n\n        GlobalConfig globalConfig = this.properties.getGlobalConfig();\n        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);\n        this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler);\n        this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler);\n        this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));\n        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);\n        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);\n        factory.setGlobalConfig(globalConfig);\n        return factory.getObject();\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            consumer.accept(this.applicationContext.getBean(clazz));\n        }\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeansThen(Class<T> clazz, Consumer<List<T>> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            final Map<String, T> beansOfType = this.applicationContext.getBeansOfType(clazz);\n            List<T> clazzList = new ArrayList<>();\n            beansOfType.forEach((k, v) -> clazzList.add(v));\n            consumer.accept(clazzList);\n        }\n    }\n\n    private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {\n        MybatisPlusProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();\n        MybatisConfiguration configuration = null;\n        if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) {\n            configuration = new MybatisConfiguration();\n        }\n        if (configuration != null && coreConfiguration != null) {\n            coreConfiguration.applyTo(configuration);\n        }\n        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {\n            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {\n                customizer.customize(configuration);\n            }\n        }\n        factory.setConfiguration(configuration);\n    }\n\n    private void applySqlSessionFactoryBeanCustomizers(MybatisSqlSessionFactoryBean factory) {\n        if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {\n            for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {\n                customizer.customize(factory);\n            }\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {\n        ExecutorType executorType = this.properties.getExecutorType();\n        if (executorType != null) {\n            return new SqlSessionTemplate(sqlSessionFactory, executorType);\n        } else {\n            return new SqlSessionTemplate(sqlSessionFactory);\n        }\n    }\n\n    /**\n     * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use\n     * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,\n     * similar to using Spring Data JPA repositories.\n     */\n    public static class AutoConfiguredMapperScannerRegistrar\n        implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {\n\n        private BeanFactory beanFactory;\n        private Environment environment;\n\n        @Override\n        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\n            if (!AutoConfigurationPackages.has(this.beanFactory)) {\n                logger.debug(\"Could not determine auto-configuration package, automatic mapper scanning disabled.\");\n                return;\n            }\n\n            logger.debug(\"Searching for mappers annotated with @Mapper\");\n\n            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);\n            if (logger.isDebugEnabled()) {\n                packages.forEach(pkg -> logger.debug(\"Using auto-configuration base package '{}'\", pkg));\n            }\n\n            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);\n            builder.addPropertyValue(\"processPropertyPlaceHolders\", true);\n            builder.addPropertyValue(\"annotationClass\", Mapper.class);\n            builder.addPropertyValue(\"basePackage\", StringUtils.collectionToCommaDelimitedString(packages));\n            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);\n            Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)\n                .collect(Collectors.toSet());\n            if (propertyNames.contains(\"lazyInitialization\")) {\n                // Need to mybatis-spring 2.0.2+\n                builder.addPropertyValue(\"lazyInitialization\", \"${mybatis-plus.lazy-initialization:${mybatis.lazy-initialization:false}}\");\n            }\n            if (propertyNames.contains(\"defaultScope\")) {\n                // Need to mybatis-spring 2.0.6+\n                builder.addPropertyValue(\"defaultScope\", \"${mybatis-plus.mapper-default-scope:}\");\n            }\n\n            // for spring-native\n            Boolean injectSqlSession = environment.getProperty(\"mybatis-plus.inject-sql-session-on-mapper-scan\", Boolean.class);\n            if (injectSqlSession == null) {\n                injectSqlSession = environment.getProperty(\"mybatis.inject-sql-session-on-mapper-scan\", Boolean.class, Boolean.TRUE);\n            }\n            if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {\n                ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;\n                Optional<String> sqlSessionTemplateBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));\n                Optional<String> sqlSessionFactoryBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));\n                if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {\n                    builder.addPropertyValue(\"sqlSessionTemplateBeanName\",\n                        sqlSessionTemplateBeanName.orElse(\"sqlSessionTemplate\"));\n                } else {\n                    builder.addPropertyValue(\"sqlSessionFactoryBeanName\", sqlSessionFactoryBeanName.get());\n                }\n            }\n            builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\n            registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());\n        }\n\n        @Override\n        public void setBeanFactory(BeanFactory beanFactory) {\n            this.beanFactory = beanFactory;\n        }\n\n        @Override\n        public void setEnvironment(Environment environment) {\n            this.environment = environment;\n        }\n\n        private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {\n            String[] beanNames = factory.getBeanNamesForType(type);\n            return beanNames.length > 0 ? beanNames[0] : null;\n        }\n    }\n\n    /**\n     * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan\n     * mappers based on the same component-scanning path as Spring Boot itself.\n     */\n    @org.springframework.context.annotation.Configuration(proxyBeanMethods = false)\n    @Import(AutoConfiguredMapperScannerRegistrar.class)\n    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})\n    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {\n\n        @Override\n        public void afterPropertiesSet() {\n            logger.debug(\n                \"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.\");\n        }\n    }\n\n    /**\n     * @deprecated 3.5.13\n     * @see MybatisSqlSessionFactoryBean#setApplicationContext(ApplicationContext)\n     * @return MybatisPlusApplicationContextAware\n     */\n    @Bean\n    @Deprecated\n    @ConditionalOnMissingBean(MybatisPlusApplicationContextAware.class)\n    public MybatisPlusApplicationContextAware mybatisPlusSpringApplicationContextAware() {\n        return new MybatisPlusApplicationContextAware();\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json",
    "content": "{\n    \"groups\": [\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.configuration\",\n            \"sourceMethod\": \"getConfiguration()\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.global-config\",\n            \"sourceMethod\": \"getGlobalConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config\",\n            \"sourceMethod\": \"getDbConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\"\n        }\n    ],\n    \"properties\": [\n        {\n            \"defaultValue\": false,\n            \"name\": \"mybatis-plus.lazy-initialization\",\n            \"description\": \"Set whether enable lazy initialization for mapper bean.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"defaultValue\": \"\",\n            \"name\": \"mybatis-plus.mapper-default-scope\",\n            \"description\": \"A default scope for mapper bean that scanned by auto-configure.\",\n            \"type\": \"java.lang.String\"\n        },\n        {\n            \"defaultValue\": true,\n            \"name\": \"mybatis-plus.inject-sql-session-on-mapper-scan\",\n            \"description\": \"Set whether inject a SqlSessionTemplate or SqlSessionFactory bean (If you want to back to the behavior of 2.2.1 or before, specify false). If you use together with spring-native, should be set true.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.identifier-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.meta-object-handler\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.sql-injector\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config.key-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.default-scripting-language-driver\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-scripting-language\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"设置无效.\",\n                \"replacement\": \"mybatis-plus.configuration.default-scripting-language-driver\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-enum-type-handler\",\n            \"defaultValue\": \"org.apache.ibatis.type.EnumTypeHandler\",\n            \"description\": \"A default TypeHandler class for Enum.\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.type.TypeHandler>\"\n        }\n    ]\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/main/resources/META-INF/spring.factories",
    "content": "# Auto Configure\norg.springframework.boot.env.EnvironmentPostProcessor=\\\n  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n# Depends On Database Initialization Detectors\norg.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisDependsOnDatabaseInitializationDetector\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/autoconfigure/ApplicationTestStartSuccess.java",
    "content": "package com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2022-11-29\n */\n@SpringBootApplication\npublic class ApplicationTestStartSuccess {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = SpringApplication.run(ApplicationTestStartSuccess.class, args);\n        SqlSessionFactory bean = context.getBean(SqlSessionFactory.class);\n        Configuration configuration = bean.getConfiguration();\n        GlobalConfig config = GlobalConfigUtils.getGlobalConfig(configuration);\n        assertThat(config.getIdentifierGenerator()).isNotNull();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/MetadataTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.core.io.FileSystemResource;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.util.Properties;\n\n/**\n * 检查元数据生成\n *\n * @author nieqiurong 2019/2/9.\n */\nclass MetadataTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Metadata {\n        private String name;\n        private String type;\n        private String sourceType;\n    }\n\n    @Test\n    void checkSpringAutoconfigureMetadataProperties() throws IOException {\n        Properties properties = new Properties();\n        properties.load(new FileSystemResource(\"build/classes/java/main/META-INF/spring-autoconfigure-metadata.properties\").getInputStream());\n        Assertions.assertEquals(DataSourceAutoConfiguration.class.getName() + \",\" + MybatisPlusLanguageDriverAutoConfiguration.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".AutoConfigureAfter\"));\n        Assertions.assertEquals(DataSource.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnSingleCandidate\"));\n        Assertions.assertEquals(SqlSessionFactory.class.getName() + \",\" + SqlSessionFactoryBean.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnClass\"));\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/MybatisPlusPropertiesTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 属性测试\n *\n * @author nieqiurong 2019/5/4.\n */\nclass MybatisPlusPropertiesTest {\n\n    @Test\n    void resolveMapperLocationsTest() {\n        MybatisPlusProperties mybatisPlusProperties = new MybatisPlusProperties();\n        //默认扫描 classpath*:/mapper/**/*.xml\n        Assertions.assertEquals(\"classpath*:/mapper/**/*.xml\", mybatisPlusProperties.getMapperLocations()[0]);\n        Assertions.assertEquals(2, mybatisPlusProperties.resolveMapperLocations().length);\n        //扫描不存在的路径\n        mybatisPlusProperties.setMapperLocations(new String[]{\"classpath:mybatis-plus/*.xml\"});\n        Assertions.assertEquals(0, mybatisPlusProperties.resolveMapperLocations().length);\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/entity/Test.java",
    "content": "package com.baomidou.mybatisplus.test.entity;\n\nimport lombok.Data;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\n@Data\npublic class Test {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/mapper/TestMapper.java",
    "content": "package com.baomidou.mybatisplus.test.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.entity.Test;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\npublic interface TestMapper extends BaseMapper<Test> {\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private String version;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\")) {\n            JerryParser jerryParser = Jerry.create(new LagartoDOMBuilder().enableXmlMode());\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), $this.s(\"version\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency mp = dependenciesMap.get(\"mybatis-plus\");\n            Assertions.assertEquals(\"compile\", mp.getScope());\n            Assertions.assertFalse(mp.isOptional());\n            Dependency autoconfigure = dependenciesMap.get(\"spring-boot-autoconfigure\");\n            Assertions.assertEquals(\"compile\", autoconfigure.getScope());\n            Assertions.assertFalse(autoconfigure.isOptional());\n            Dependency jdbc = dependenciesMap.get(\"spring-boot-starter-jdbc\");\n            Assertions.assertEquals(\"compile\", jdbc.getScope());\n            Assertions.assertFalse(jdbc.isOptional());\n            Dependency configurationProcessor = dependenciesMap.get(\"spring-boot-configuration-processor\");\n            Assertions.assertEquals(\"compile\", configurationProcessor.getScope());\n            Assertions.assertTrue(configurationProcessor.isOptional());\n            Dependency autoconfigureProcessor = dependenciesMap.get(\"spring-boot-autoconfigure-processor\");\n            Assertions.assertEquals(\"compile\", autoconfigureProcessor.getScope());\n            Assertions.assertTrue(autoconfigureProcessor.isOptional());\n            Dependency bom = dependenciesMap.get(\"spring-boot-dependencies\");\n            Assertions.assertEquals(\"import\", bom.getScope());\n            Assertions.assertFalse(bom.isOptional());\n            Assertions.assertEquals(\"3.1.8\", dependenciesMap.get(\"spring-cloud-commons\").getVersion());\n            Assertions.assertEquals(\"2.1.2\", dependenciesMap.get(\"mybatis-spring\").getVersion());\n            Assertions.assertEquals(\"2.7.18\", dependenciesMap.get(\"spring-boot-dependencies\").getVersion());\n        }\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/resources/application.yml",
    "content": "spring:\n    datasource:\n        driver-class-name: org.h2.Driver\n        url: jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\n        username: sa\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/resources/mapper/modulea/testa.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter/src/test/resources/mapper/moduleb/testb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/build.gradle",
    "content": "// 全量 copy 自 mybatis 的 mybatis-spring-boot-starter-test\n\ndependencies {\n    api project(\":spring-boot-starter:mybatis-plus-boot-starter\")\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-test-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBootVersion}\")\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-test\"\n    api \"org.springframework:spring-tx\"\n    testImplementation \"${lib.h2}\"\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/main/resources/META-INF/spring/com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports",
    "content": "# AutoConfigureMybatis auto-configuration imports\norg.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration\norg.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration\norg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/main/resources/META-INF/spring.factories",
    "content": "# AutoConfigureMybatis auto-configuration imports\ncom.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus=\\\norg.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\\\norg.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\\\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\\\norg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusSampleTest.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n//@MybatisPlusTest(properties = \"spring.datasource.schema=classpath:schema.sql\")\n@MybatisPlusTest\nclass MybatisPlusSampleTest {\n\n    @Autowired\n    private SampleMapper sampleMapper;\n\n    @Test\n    void testInsert() {\n        Sample sample = new Sample();\n        sampleMapper.insert(sample);\n        assertThat(sample.getId()).isNotNull();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTestApplication.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@SpringBootApplication\npublic class MybatisPlusTestApplication {\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/Sample.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Data\npublic class Sample {\n    private Long id;\n    private String name;\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/SampleMapper.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Mapper\npublic interface SampleMapper extends BaseMapper<Sample> {\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/resources/application.yml",
    "content": "spring:\n    datasource:\n        schema: classpath:schema.sql\n\nlogging:\n    level:\n        org.springframework.jdbc: debug\n        com.baomidou.mybatisplus.test.autoconfigure: debug\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-boot-starter-test/src/test/resources/schema.sql",
    "content": "drop table if exists sample;\ncreate table if not exists sample\n(\n    id   bigint,\n    name varchar\n);\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/build.gradle",
    "content": "dependencies {\n    implementation project(\":mybatis-plus\")\n    implementation \"${lib.\"mybatis-spring\"}\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}\"\n    implementation \"org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}\"\n    annotationProcessor \"org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}\"\n    annotationProcessor \"org.springframework.boot:spring-boot-autoconfigure-processor:${springBootVersion}\"\n    implementation \"${lib['mybatis-thymeleaf']}\"\n    implementation \"${lib.'mybatis-velocity'}\"\n    implementation \"${lib.'mybatis-freemarker'}\"\n    implementation \"${lib.'spring-cloud-commons'}\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test:${springBootVersion}\"\n    testImplementation \"${lib.'mybatis-spring-boot-starter'}\"\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/ConfigurationCustomizer.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\n\n/**\n * Callback interface that can be customized a {@link MybatisConfiguration} object generated on auto-configuration.\n *\n * @author Kazuki Shimizu\n * @since 1.2.1\n */\n@FunctionalInterface\npublic interface ConfigurationCustomizer {\n\n    /**\n     * Customize the given a {@link MybatisConfiguration} object.\n     *\n     * @param configuration the configuration object to customize\n     */\n    void customize(MybatisConfiguration configuration);\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/DdlApplicationRunner.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.extension.ddl.DdlHelper;\nimport com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler;\nimport com.baomidou.mybatisplus.extension.ddl.IDdl;\nimport lombok.Setter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.ibatis.jdbc.ScriptRunner;\nimport org.springframework.aop.support.AopUtils;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\n/**\n * DDL 启动应用后执行\n *\n * @author hubin\n * @since 2021-06-22\n */\n@Slf4j\npublic class DdlApplicationRunner implements ApplicationRunner {\n\n    /**\n     * 处理器列表\n     */\n    private final List<IDdl> ddlList;\n\n    /**\n     * 是否自动提交 (默认自动提交)\n     *\n     * @since 3.5.11\n     */\n    @Setter\n    private boolean autoCommit = true;\n\n    /**\n     * 执行脚本错误处理器 (默认打印错误日志继续执行下一个文件)\n     *\n     * @since 3.5.11\n     */\n    @Setter\n    private DdlScriptErrorHandler ddlScriptErrorHandler = DdlScriptErrorHandler.PrintlnLogErrorHandler.INSTANCE;\n\n    /**\n     * 自定义 ScriptRunner 函数\n     *\n     * @since 3.5.11\n     */\n    @Setter\n    private Consumer<ScriptRunner> scriptRunnerConsumer;\n\n    /**\n     * 是否抛出异常\n     * <p>注意这里是控制{@link #ddlList}循环处理时是否抛出异常</p>\n     * <p>当设置为false时,会遍历处理完所有处理器</p>\n     * <p>当设置为true时,在遍历处理时遇到异常会抛出异常中断下一个处理器处理</p>\n     *\n     * @since 3.5.11 保持兼容性,默认不抛出\n     */\n    @Setter\n    private boolean throwException = false;\n\n    public DdlApplicationRunner(List<IDdl> ddlList) {\n        this.ddlList = ddlList;\n    }\n\n    @Override\n    public void run(ApplicationArguments args) {\n        if (CollectionUtils.isNotEmpty(ddlList)) {\n            log.debug(\"  ...  DDL start create  ...  \");\n            ddlList.forEach(ddl -> ddl.runScript(dataSource -> {\n                String ddlClassName = AopUtils.getTargetClass(ddl).getName();\n                if (CollectionUtils.isEmpty(ddl.getSqlFiles())) {\n                    log.warn(\"{}, sql files is empty\", ddlClassName);\n                    return;\n                }\n                log.info(\"{}, run sql files {}\", ddlClassName, ddl.getSqlFiles());\n                try {\n                    DdlHelper.runScript(ddl.getDdlGenerator(),\n                        dataSource, ddl.getSqlFiles(), this.scriptRunnerConsumer, this.autoCommit, this.ddlScriptErrorHandler);\n                } catch (Exception e) {\n                    log.error(\"{}, run sql file error: \", ddlClassName, e);\n                    if (throwException) {\n                        throw new RuntimeException(e);\n                    }\n                }\n            }));\n            log.debug(\"  ...  DDL end create  ...  \");\n        }\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/DdlAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.extension.ddl.IDdl;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n * @since 3.5.7\n */\n@ConditionalOnClass(IDdl.class)\n@Configuration(proxyBeanMethods = false)\npublic class DdlAutoConfiguration {\n\n    @Bean\n    @Order\n    @ConditionalOnBean({IDdl.class})\n    @ConditionalOnMissingBean({DdlApplicationRunner.class})\n    public DdlApplicationRunner ddlApplicationRunner(List<IDdl> ddlList) {\n        return new DdlApplicationRunner(ddlList);\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/IdentifierGeneratorAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.logging.LogFactory;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.cloud.commons.util.InetUtils;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * @author nieqiurong 2021/1/29\n * @since 3.4.3\n */\n@Lazy\n@ConditionalOnClass(InetUtils.class)\n@ConditionalOnBean(InetUtils.class)\n@Configuration(proxyBeanMethods = false)\npublic class IdentifierGeneratorAutoConfiguration {\n\n    @Bean\n    @ConditionalOnMissingBean\n    public IdentifierGenerator identifierGenerator(InetUtils inetUtils) {\n        try {\n            return new DefaultIdentifierGenerator(inetUtils.findFirstNonLoopbackAddress());\n        } catch (Exception e) {\n            Log log = LogFactory.getLog(IdentifierGeneratorAutoConfiguration.class);\n            return DefaultIdentifierGenerator.getFixedIdentifierGenerator(log);\n        }\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisDependsOnDatabaseInitializationDetector.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;\nimport org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * {@link DependsOnDatabaseInitializationDetector} for Mybatis.\n *\n * @author Eddú Meléndez\n *\n * @since 2.3.0\n */\nclass MybatisDependsOnDatabaseInitializationDetector\n    extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {\n\n  @Override\n  protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {\n    return Collections.singleton(SqlSessionTemplate.class);\n  }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusInnerInterceptorAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.List;\n\n/**\n * @author nieqiurong\n * @since 3.5.4\n */\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnBean(InnerInterceptor.class)\n@ConditionalOnMissingBean(MybatisPlusInterceptor.class)\npublic class MybatisPlusInnerInterceptorAutoConfiguration {\n\n    @Bean\n    public MybatisPlusInterceptor defaultMybatisPlusInterceptor(List<InnerInterceptor> innerInterceptorList) {\n        MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();\n        plusInterceptor.setInterceptors(innerInterceptorList);\n        return plusInterceptor;\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusLanguageDriverAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.MybatisParameterHandler;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.extension.scripting.MybatisFreeMarkerLanguageDriver;\nimport com.baomidou.mybatisplus.extension.scripting.MybatisThymeleafLanguageDriver;\nimport com.baomidou.mybatisplus.extension.scripting.MybatisVelocityLanguageDriver;\nimport org.apache.ibatis.executor.parameter.ParameterHandler;\nimport org.apache.ibatis.mapping.BoundSql;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.mybatis.scripting.freemarker.FreeMarkerLanguageDriver;\nimport org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig;\nimport org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver;\nimport org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriverConfig;\nimport org.mybatis.scripting.velocity.VelocityLanguageDriver;\nimport org.mybatis.scripting.velocity.VelocityLanguageDriverConfig;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Lazy;\n\n/**\n * {@link EnableAutoConfiguration Auto-Configuration} for MyBatis's scripting language drivers.\n * <p> copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration}</p>\n *\n * @author miemie\n * @since 2019-10-22\n */\n@Lazy\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnClass(LanguageDriver.class)\npublic class MybatisPlusLanguageDriverAutoConfiguration {\n\n    private static final String CONFIGURATION_PROPERTY_PREFIX = Constants.MYBATIS_PLUS + \".scripting-language-driver\";\n\n    /**\n     * Configuration class for mybatis-freemarker 1.1.x or under.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnClass(FreeMarkerLanguageDriver.class)\n    @ConditionalOnMissingClass(\"org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig\")\n    public static class LegacyFreeMarkerConfiguration {\n        @Bean\n        @ConditionalOnMissingBean\n        FreeMarkerLanguageDriver freeMarkerLanguageDriver() {\n            return new MybatisFreeMarkerLanguageDriver();\n        }\n    }\n\n    /**\n     * Configuration class for mybatis-freemarker 1.2.x or above.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class})\n    public static class FreeMarkerConfiguration {\n        @Bean\n        @ConditionalOnMissingBean\n        FreeMarkerLanguageDriver freeMarkerLanguageDriver(FreeMarkerLanguageDriverConfig config) {\n            return new MybatisFreeMarkerLanguageDriver(config);\n        }\n\n        @Bean\n        @ConditionalOnMissingBean\n        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + \".freemarker\")\n        public FreeMarkerLanguageDriverConfig freeMarkerLanguageDriverConfig() {\n            return FreeMarkerLanguageDriverConfig.newInstance();\n        }\n    }\n\n    /**\n     * Configuration class for mybatis-velocity 2.0 or under.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnClass(org.mybatis.scripting.velocity.Driver.class)\n    @ConditionalOnMissingClass(\"org.mybatis.scripting.velocity.VelocityLanguageDriverConfig\")\n    @SuppressWarnings(\"deprecation\")\n    public static class LegacyVelocityConfiguration {\n\n        @Bean\n        @ConditionalOnMissingBean\n        org.mybatis.scripting.velocity.Driver velocityLanguageDriver() {\n            return new org.mybatis.scripting.velocity.Driver() {\n                @Override\n                public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {\n                    return new MybatisParameterHandler(mappedStatement, parameterObject, boundSql);\n                }\n            };\n        }\n\n    }\n\n    /**\n     * Configuration class for mybatis-velocity 2.1.x or above.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class})\n    public static class VelocityConfiguration {\n        @Bean\n        @ConditionalOnMissingBean\n        VelocityLanguageDriver velocityLanguageDriver(VelocityLanguageDriverConfig config) {\n            return new MybatisVelocityLanguageDriver(config);\n        }\n\n        @Bean\n        @ConditionalOnMissingBean\n        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + \".velocity\")\n        public VelocityLanguageDriverConfig velocityLanguageDriverConfig() {\n            return VelocityLanguageDriverConfig.newInstance();\n        }\n    }\n\n    @Configuration(proxyBeanMethods = false)\n    @ConditionalOnClass(ThymeleafLanguageDriver.class)\n    public static class ThymeleafConfiguration {\n        @Bean\n        @ConditionalOnMissingBean\n        ThymeleafLanguageDriver thymeleafLanguageDriver(ThymeleafLanguageDriverConfig config) {\n            return new MybatisThymeleafLanguageDriver(config);\n        }\n\n        @Bean\n        @ConditionalOnMissingBean\n        @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + \".thymeleaf\")\n        public ThymeleafLanguageDriverConfig thymeleafLanguageDriverConfig() {\n            return ThymeleafLanguageDriverConfig.newInstance();\n        }\n\n        // This class provides to avoid the https://github.com/spring-projects/spring-boot/issues/21626 as workaround.\n        @SuppressWarnings(\"unused\")\n        private static class MetadataThymeleafLanguageDriverConfig extends ThymeleafLanguageDriverConfig {\n\n            @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + \".thymeleaf.dialect\")\n            @Override\n            public DialectConfig getDialect() {\n                return super.getDialect();\n            }\n\n            @ConfigurationProperties(CONFIGURATION_PROPERTY_PREFIX + \".thymeleaf.template-file\")\n            @Override\n            public TemplateFileConfig getTemplateFile() {\n                return super.getTemplateFile();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusProperties.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.Constants;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport lombok.Data;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.experimental.Accessors;\nimport org.apache.ibatis.io.VFS;\nimport org.apache.ibatis.logging.Log;\nimport org.apache.ibatis.mapping.ResultSetType;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.AutoMappingBehavior;\nimport org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.LocalCacheScope;\nimport org.apache.ibatis.type.JdbcType;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.context.properties.NestedConfigurationProperty;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\n\nimport java.io.IOException;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\n/**\n * Configuration properties for MyBatis.\n *\n * @author Eddú Meléndez\n * @author Kazuki Shimizu\n */\n@Data\n@Accessors(chain = true)\n@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS)\npublic class MybatisPlusProperties {\n\n    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();\n\n    /**\n     * Location of MyBatis xml config file.\n     */\n    private String configLocation;\n\n    /**\n     * Locations of MyBatis mapper files.\n     *\n     * @since 3.1.2 add default value\n     */\n    private String[] mapperLocations = new String[]{\"classpath*:/mapper/**/*.xml\"};\n\n    /**\n     * Packages to search type aliases. (Package delimiters are \",; \\t\\n\")\n     */\n    private String typeAliasesPackage;\n\n    /**\n     * The super class for filtering type alias.\n     * If this not specifies, the MyBatis deal as type alias all classes that searched from typeAliasesPackage.\n     */\n    private Class<?> typeAliasesSuperType;\n\n    /**\n     * Packages to search for type handlers. (Package delimiters are \",; \\t\\n\")\n     */\n    private String typeHandlersPackage;\n\n    /**\n     * Indicates whether perform presence check of the MyBatis xml config file.\n     */\n    private boolean checkConfigLocation = false;\n\n    /**\n     * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.\n     */\n    private ExecutorType executorType;\n\n    /**\n     * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)\n     * <p>\n     * 如果设置了这个,你会至少失去几乎所有 mp 提供的功能\n     */\n    private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;\n\n    /**\n     * Externalized properties for MyBatis configuration.\n     */\n    private Properties configurationProperties;\n\n    /**\n     * A Configuration object for customize default settings. If {@link #configLocation}\n     * is specified, this property is not used.\n     */\n    private CoreConfiguration configuration;\n\n    /**\n     * 全局配置\n     */\n    @NestedConfigurationProperty\n    private GlobalConfig globalConfig = GlobalConfigUtils.defaults();\n\n\n    public Resource[] resolveMapperLocations() {\n        return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))\n            .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new);\n    }\n\n    private Resource[] getResources(String location) {\n        try {\n            return resourceResolver.getResources(location);\n        } catch (IOException e) {\n            return new Resource[0];\n        }\n    }\n\n    /**\n     * The configuration properties for mybatis core module.\n     * 虽然为高版本新增开始,但为了美化配置提示,这里也在SpringBoot2上使用.\n     *\n     * @since 3.0.0\n     */\n    @Getter\n    @Setter\n    public static class CoreConfiguration {\n\n        /**\n         * Allows using RowBounds on nested statements. If allowed, set the false. Default is false.\n         */\n        private Boolean safeRowBoundsEnabled;\n\n        /**\n         * Allows using ResultHandler on nested statements. If allowed, set the false. Default is true.\n         */\n        private Boolean safeResultHandlerEnabled;\n\n        /**\n         * Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names\n         * aColumn. Default is false.\n         */\n        private Boolean mapUnderscoreToCamelCase;\n\n        /**\n         * When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded\n         * on demand (see also lazyLoadTriggerMethods). Default is false.\n         */\n        private Boolean aggressiveLazyLoading;\n\n        /**\n         * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required).\n         * Default is true.\n         */\n        private Boolean multipleResultSetsEnabled;\n\n        /**\n         * Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be\n         * used if set to true, as some drivers deny compatibility but still work (e.g. Derby). Default is false.\n         */\n        private Boolean useGeneratedKeys;\n\n        /**\n         * Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to\n         * the driver documentation, or test out both modes to determine how your driver behaves. Default is true.\n         */\n        private Boolean useColumnLabel;\n\n        /**\n         * Globally enables or disables any caches configured in any mapper under this configuration. Default is true.\n         */\n        private Boolean cacheEnabled;\n\n        /**\n         * Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you\n         * rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to\n         * null. Default is false.\n         */\n        private Boolean callSettersOnNulls;\n\n        /**\n         * Allow referencing statement parameters by their actual names declared in the method signature. To use this\n         * feature, your project must be compiled in Java 8 with -parameters option. Default is true.\n         */\n        private Boolean useActualParamName;\n\n        /**\n         * MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled,\n         * MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collection and\n         * association). Default is false.\n         */\n        private Boolean returnInstanceForEmptyRow;\n\n        /**\n         * Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. Default is\n         * false.\n         */\n        private Boolean shrinkWhitespacesInSql;\n\n        /**\n         * Specifies the default value of 'nullable' attribute on 'foreach' tag. Default is false.\n         */\n        private Boolean nullableOnForEach;\n\n        /**\n         * When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on\n         * the column order. Default is false.\n         */\n        private Boolean argNameBasedConstructorAutoMapping;\n\n        /**\n         * Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be\n         * superseded for a specific relation by using the fetchType attribute on it. Default is False.\n         */\n        private Boolean lazyLoadingEnabled;\n\n        /**\n         * Sets the number of seconds the driver will wait for a response from the database.\n         */\n        private Integer defaultStatementTimeout;\n\n        /**\n         * Sets the driver a hint as to control fetching size for return results. This parameter value can be overridden by a\n         * query setting.\n         */\n        private Integer defaultFetchSize;\n\n        /**\n         * MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default,\n         * (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be\n         * used just for statement execution, no data will be shared between two different calls to the same SqlSession.\n         * Default is SESSION.\n         */\n        private LocalCacheScope localCacheScope;\n\n        /**\n         * Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers\n         * require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. Default\n         * is OTHER.\n         */\n        private JdbcType jdbcTypeForNull;\n\n        /**\n         * Specifies a scroll strategy when omit it per statement settings.\n         */\n        private ResultSetType defaultResultSetType;\n\n        /**\n         * Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements.\n         * BATCH executor reuses statements and batches updates. Default is SIMPLE.\n         */\n        private ExecutorType defaultExecutorType;\n\n        /**\n         * Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping.\n         * PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result\n         * mappings of any complexity (containing nested or otherwise). Default is PARTIAL.\n         */\n        private AutoMappingBehavior autoMappingBehavior;\n\n        /**\n         * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target.\n         * Default is NONE.\n         */\n        private AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;\n\n        /**\n         * Specifies the prefix string that MyBatis will add to the logger names.\n         */\n        private String logPrefix;\n\n        /**\n         * Specifies which Object's methods trigger a lazy load. Default is [equals,clone,hashCode,toString].\n         */\n        private Set<String> lazyLoadTriggerMethods;\n\n        /**\n         * Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation\n         * will be discovered.\n         */\n        private Class<? extends Log> logImpl;\n\n        /**\n         * Specifies VFS implementations.\n         */\n        private Class<? extends VFS> vfsImpl;\n\n        /**\n         * Specifies a sql provider class that holds provider method. This class apply to the type(or value) attribute on\n         * sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.\n         */\n        private Class<?> defaultSqlProviderType;\n\n        /**\n         * Specifies the TypeHandler used by default for Enum.\n         */\n        Class<? extends TypeHandler> defaultEnumTypeHandler;\n\n        /**\n         * Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to\n         * load lazy properties of deserialized objects. This class must have a method with a signature static Configuration\n         * getConfiguration().\n         */\n        private Class<?> configurationFactory;\n\n        /**\n         * Specify any configuration variables.\n         */\n        private Properties variables;\n\n        /**\n         * Specifies the database identify value for switching query to use.\n         */\n        private String databaseId;\n\n        // 新增兼容开始... mybatis 3.x 的有做属性删减  部分属性在2.x可用 3.x 已经被剔除了.\n        /**\n         * Specifies the language used by default for dynamic SQL generation.\n         */\n        private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;\n\n        private Boolean useGeneratedShortKey;\n\n        public void applyTo(MybatisConfiguration target) {\n            Optional.ofNullable(getSafeRowBoundsEnabled()).ifPresent(target::setSafeRowBoundsEnabled);\n            Optional.ofNullable(getSafeResultHandlerEnabled()).ifPresent(target::setSafeResultHandlerEnabled);\n            Optional.ofNullable(getMapUnderscoreToCamelCase()).ifPresent(target::setMapUnderscoreToCamelCase);\n            Optional.ofNullable(getAggressiveLazyLoading()).ifPresent(target::setAggressiveLazyLoading);\n            Optional.ofNullable(getMultipleResultSetsEnabled()).ifPresent(target::setMultipleResultSetsEnabled);\n            Optional.ofNullable(getUseGeneratedKeys()).ifPresent(target::setUseGeneratedKeys);\n            Optional.ofNullable(getUseColumnLabel()).ifPresent(target::setUseColumnLabel);\n            Optional.ofNullable(getCacheEnabled()).ifPresent(target::setCacheEnabled);\n            Optional.ofNullable(getCallSettersOnNulls()).ifPresent(target::setCallSettersOnNulls);\n            Optional.ofNullable(getUseActualParamName()).ifPresent(target::setUseActualParamName);\n            Optional.ofNullable(getReturnInstanceForEmptyRow()).ifPresent(target::setReturnInstanceForEmptyRow);\n            Optional.ofNullable(getShrinkWhitespacesInSql()).ifPresent(target::setShrinkWhitespacesInSql);\n            Optional.ofNullable(getNullableOnForEach()).ifPresent(target::setNullableOnForEach);\n            Optional.ofNullable(getArgNameBasedConstructorAutoMapping()).ifPresent(target::setArgNameBasedConstructorAutoMapping);\n            Optional.ofNullable(getLazyLoadingEnabled()).ifPresent(target::setLazyLoadingEnabled);\n            Optional.ofNullable(getLogPrefix()).ifPresent(target::setLogPrefix);\n            Optional.ofNullable(getLazyLoadTriggerMethods()).ifPresent(target::setLazyLoadTriggerMethods);\n            Optional.ofNullable(getDefaultStatementTimeout()).ifPresent(target::setDefaultStatementTimeout);\n            Optional.ofNullable(getDefaultFetchSize()).ifPresent(target::setDefaultFetchSize);\n            Optional.ofNullable(getLocalCacheScope()).ifPresent(target::setLocalCacheScope);\n            Optional.ofNullable(getJdbcTypeForNull()).ifPresent(target::setJdbcTypeForNull);\n            Optional.ofNullable(getDefaultResultSetType()).ifPresent(target::setDefaultResultSetType);\n            Optional.ofNullable(getDefaultExecutorType()).ifPresent(target::setDefaultExecutorType);\n            Optional.ofNullable(getAutoMappingBehavior()).ifPresent(target::setAutoMappingBehavior);\n            Optional.ofNullable(getAutoMappingUnknownColumnBehavior()).ifPresent(target::setAutoMappingUnknownColumnBehavior);\n            Optional.ofNullable(getVariables()).ifPresent(target::setVariables);\n            Optional.ofNullable(getLogImpl()).ifPresent(target::setLogImpl);\n            Optional.ofNullable(getVfsImpl()).ifPresent(target::setVfsImpl);\n            Optional.ofNullable(getDefaultSqlProviderType()).ifPresent(target::setDefaultSqlProviderType);\n            Optional.ofNullable(getConfigurationFactory()).ifPresent(target::setConfigurationFactory);\n            Optional.ofNullable(getDefaultEnumTypeHandler()).ifPresent(target::setDefaultEnumTypeHandler);\n            Optional.ofNullable(getDefaultScriptingLanguageDriver()).ifPresent(target::setDefaultScriptingLanguage);\n            Optional.ofNullable(getDatabaseId()).ifPresent(target::setDatabaseId);\n            Optional.ofNullable(getUseGeneratedShortKey()).ifPresent(target::setUseGeneratedShortKey);\n        }\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusPropertiesCustomizer.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\n/**\n * Callback interface that can be customized a {@link MybatisPlusProperties} object generated on auto-configuration.\n *\n * <p> 慎用 </p>\n *\n * @author miemie\n * @since 3.1.2\n */\n@FunctionalInterface\npublic interface MybatisPlusPropertiesCustomizer {\n\n    /**\n     * Customize the given a {@link MybatisPlusProperties} object.\n     *\n     * @param properties the MybatisPlusProperties object to customize\n     */\n    void customize(MybatisPlusProperties properties);\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/SafetyEncryptProcessor.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.toolkit.AES;\nimport com.baomidou.mybatisplus.core.toolkit.CollectionUtils;\nimport com.baomidou.mybatisplus.core.toolkit.StringUtils;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.env.EnvironmentPostProcessor;\nimport org.springframework.boot.env.OriginTrackedMapPropertySource;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.core.env.MapPropertySource;\nimport org.springframework.core.env.PropertySource;\n\nimport java.util.HashMap;\n\n/**\n * 安全加密处理器\n *\n * @author hubin\n * @since 2020-05-23\n */\npublic class SafetyEncryptProcessor implements EnvironmentPostProcessor {\n\n    @Override\n    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {\n        String mpwKey = environment.getProperty(\"mpw.key\");\n        /**\n         * 处理加密内容\n         */\n        if (StringUtils.isNotBlank(mpwKey)) {\n            HashMap<String, Object> map = new HashMap<>();\n            for (PropertySource<?> ps : environment.getPropertySources()) {\n                if (ps instanceof OriginTrackedMapPropertySource) {\n                    OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) ps;\n                    for (String name : source.getPropertyNames()) {\n                        Object value = source.getProperty(name);\n                        if (value instanceof String) {\n                            String str = (String) value;\n                            String resolvedValue = environment.resolveRequiredPlaceholders(str);\n                            if (resolvedValue.startsWith(\"mpw:\")) {\n                                map.put(name, AES.decrypt(resolvedValue.substring(4), mpwKey));\n                            }\n                        }\n                    }\n                }\n            }\n            // 将解密的数据放入环境变量，并处于第一优先级上\n            if (CollectionUtils.isNotEmpty(map)) {\n                environment.getPropertySources().addFirst(new MapPropertySource(\"custom-encrypt\", map));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/SpringBootVFS.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\n\nimport org.apache.ibatis.io.VFS;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.util.ClassUtils;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.nio.charset.Charset;\nimport java.text.Normalizer;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * @author Hans Westerbeek\n * @author Eddú Meléndez\n * @author Kazuki Shimizu\n */\npublic class SpringBootVFS extends VFS {\n\n    private static Charset urlDecodingCharset;\n    private static Supplier<ClassLoader> classLoaderSupplier;\n    private final ResourcePatternResolver resourceResolver;\n\n    static {\n        setUrlDecodingCharset(Charset.defaultCharset());\n        setClassLoaderSupplier(ClassUtils::getDefaultClassLoader);\n    }\n\n    public SpringBootVFS() {\n        this.resourceResolver = new PathMatchingResourcePatternResolver(classLoaderSupplier.get());\n    }\n\n    @Override\n    public boolean isValid() {\n        return true;\n    }\n\n    @Override\n    protected List<String> list(URL url, String path) throws IOException {\n        String urlString = URLDecoder.decode(url.toString(), urlDecodingCharset.name());\n        String baseUrlString = urlString.endsWith(\"/\") ? urlString : urlString.concat(\"/\");\n        Resource[] resources = resourceResolver.getResources(baseUrlString + \"**/*.class\");\n        return Stream.of(resources).map(resource -> preserveSubpackageName(baseUrlString, resource, path))\n            .collect(Collectors.toList());\n    }\n\n    /**\n     * Set the charset for decoding an encoded URL string.\n     * <p>\n     * Default is system default charset.\n     * </p>\n     *\n     * @param charset the charset for decoding an encoded URL string\n     * @since 2.3.0\n     */\n    public static void setUrlDecodingCharset(Charset charset) {\n        urlDecodingCharset = charset;\n    }\n\n    /**\n     * Set the supplier for providing {@link ClassLoader} to used.\n     * <p>\n     * Default is a returned instance from {@link ClassUtils#getDefaultClassLoader()}.\n     * </p>\n     *\n     * @param supplier the supplier for providing {@link ClassLoader} to used\n     * @since 3.0.2\n     */\n    public static void setClassLoaderSupplier(Supplier<ClassLoader> supplier) {\n        classLoaderSupplier = supplier;\n    }\n\n    private static String preserveSubpackageName(final String baseUrlString, final Resource resource,\n                                                 final String rootPath) {\n        try {\n            return rootPath + (rootPath.endsWith(\"/\") ? \"\" : \"/\") + Normalizer\n                .normalize(URLDecoder.decode(resource.getURL().toString(), urlDecodingCharset.name()), Normalizer.Form.NFC)\n                .substring(baseUrlString.length());\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/SqlSessionFactoryBeanCustomizer.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\n\n/**\n * Callback interface that can be customized a {@link MybatisSqlSessionFactoryBean} object generated on auto-configuration.\n *\n * @author Kazuki Shimizu\n * @since 2.2.2\n */\n@FunctionalInterface\npublic interface SqlSessionFactoryBeanCustomizer {\n\n    /**\n     * Customize the given a {@link MybatisSqlSessionFactoryBean} object.\n     *\n     * @param factoryBean the factory bean object to customize\n     */\n    void customize(MybatisSqlSessionFactoryBean factoryBean);\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/main/java/com/baomidou/mybatisplus/autoconfigure/package-info.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Spring Boot Stater\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/test/java/com/baomidou/mybatisplus/test/MetadataTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.jayway.jsonpath.DocumentContext;\nimport com.jayway.jsonpath.JsonPath;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.apache.ibatis.type.JdbcType;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.core.io.FileSystemResource;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * 检查元数据生成\n *\n * @author nieqiurong 2019/2/9.\n */\nclass MetadataTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Metadata {\n        private String name;\n        private String type;\n        private String sourceType;\n    }\n\n    @Test\n    void checkSpringConfigurationMetadataJson() throws IOException {\n        DocumentContext documentContext = JsonPath.parse(new FileSystemResource(\"build/classes/java/main/META-INF/spring-configuration-metadata.json\").getInputStream());\n        List<Map<String, String>> properties = documentContext.read(\"$.properties\");\n        Map<String, Metadata> metadataMap = properties.stream().map(map -> new Metadata(map.get(\"name\"), map.get(\"type\"), map.get(\"sourceType\"))).collect(Collectors.toMap(Metadata::getName, metadata -> metadata));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.type-aliases-package\"), new Metadata(\"mybatis-plus.type-aliases-package\", String.class.getName(), MybatisPlusProperties.class.getName()));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.global-config.db-config.table-underline\"), new Metadata(\"mybatis-plus.global-config.db-config.table-underline\", Boolean.class.getName(), GlobalConfig.DbConfig.class.getName()));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.config-location\"), new Metadata(\"mybatis-plus.config-location\", String.class.getName(), MybatisPlusProperties.class.getName()));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.configuration.call-setters-on-nulls\"), new Metadata(\"mybatis-plus.configuration.call-setters-on-nulls\", Boolean.class.getName(), MybatisPlusProperties.CoreConfiguration.class.getName()));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.configuration.jdbc-type-for-null\"), new Metadata(\"mybatis-plus.configuration.jdbc-type-for-null\", JdbcType.class.getName(), MybatisPlusProperties.CoreConfiguration.class.getName()));\n        Assertions.assertEquals(metadataMap.get(\"mybatis-plus.configuration.map-underscore-to-camel-case\"), new Metadata(\"mybatis-plus.configuration.map-underscore-to-camel-case\", Boolean.class.getName(), MybatisPlusProperties.CoreConfiguration.class.getName()));\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-autoconfigure/src/test/java/com/baomidou/mybatisplus/test/MybatisPlusPropertiesTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @author nieqiurong\n */\npublic class MybatisPlusPropertiesTest {\n\n    @Test\n    void testValue() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        MybatisPlusProperties.CoreConfiguration coreConfiguration = new MybatisPlusProperties.CoreConfiguration();\n        coreConfiguration.setCacheEnabled(true);\n        coreConfiguration.setDatabaseId(\"test\");\n        coreConfiguration.setDefaultFetchSize(10);\n        coreConfiguration.applyTo(configuration);\n        Assertions.assertTrue(configuration.isCacheEnabled());\n        Assertions.assertEquals(\"test\", configuration.getDatabaseId());\n        Assertions.assertEquals(10, configuration.getDefaultFetchSize());\n    }\n\n    @Test\n    void testNull() {\n        MybatisConfiguration configuration = new MybatisConfiguration();\n        configuration.setCacheEnabled(true);\n        configuration.setDatabaseId(\"test\");\n        configuration.setDefaultFetchSize(10);\n        MybatisPlusProperties.CoreConfiguration coreConfiguration = new MybatisPlusProperties.CoreConfiguration();\n        coreConfiguration.applyTo(configuration);\n        Assertions.assertTrue(configuration.isCacheEnabled());\n        Assertions.assertEquals(\"test\", configuration.getDatabaseId());\n        Assertions.assertEquals(10, configuration.getDefaultFetchSize());\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-test-autoconfigure/build.gradle",
    "content": "dependencies {\n    implementation \"org.springframework:spring-tx:${springVersion}\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}\"\n    implementation \"org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}\"\n    implementation \"org.springframework.boot:spring-boot-starter-test:${springBootVersion}\"\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-test-autoconfigure/src/main/java/com/baomidou/mybatisplus/test/autoconfigure/AutoConfigureMybatisPlus.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\n\nimport java.lang.annotation.*;\n\n/**\n * {@link ImportAutoConfiguration Auto-configuration imports} for typical Mybatis tests. Most tests should consider\n * using {@link MybatisPlusTest @MybatisTest} rather than using this annotation directly.\n *\n * @author miemie\n * @since 2020-05-27\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@ImportAutoConfiguration\npublic @interface AutoConfigureMybatisPlus {\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-test-autoconfigure/src/main/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTest.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;\nimport org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;\nimport org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;\nimport org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.annotation.ComponentScan.Filter;\nimport org.springframework.core.annotation.AliasFor;\nimport org.springframework.core.env.Environment;\nimport org.springframework.test.context.BootstrapWith;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}(JUnit 4) and\n * {@code @ExtendWith(SpringExtension.class)}(JUnit 5) for a typical mybatis test. Can be used when a test focuses\n * <strong>only</strong> on mybatis-based components. Since 2.0.1, If you use this annotation on JUnit 5,\n * {@code @ExtendWith(SpringExtension.class)} can omit on your test class.\n * <p>\n * Using this annotation will disable full auto-configuration and instead apply only configuration relevant to mybatis\n * tests.\n * <p>\n * By default, tests annotated with {@code @MybatisTest} will use an embedded in-memory database (replacing any explicit\n * or usually auto-configured DataSource). The {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} annotation\n * can be used to override these settings.\n * <p>\n * If you are looking to load your full application configuration, but use an embedded database, you should consider\n * {@link SpringBootTest @SpringBootTest} combined with {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase}\n * rather than this annotation.\n *\n * @author miemie\n * @since 2020-05-27\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@BootstrapWith(MybatisPlusTestContextBootstrapper.class)\n@ExtendWith(SpringExtension.class)\n@OverrideAutoConfiguration(enabled = false)\n@TypeExcludeFilters(MybatisPlusTypeExcludeFilter.class)\n@Transactional\n@AutoConfigureCache\n@AutoConfigureMybatisPlus\n@AutoConfigureTestDatabase\n@ImportAutoConfiguration\npublic @interface MybatisPlusTest {\n\n    /**\n     * Properties in form {@literal key=value} that should be added to the Spring {@link Environment} before the test\n     * runs.\n     *\n     * @return the properties to add\n     * @since 2.1.0\n     */\n    String[] properties() default {};\n\n    /**\n     * Determines if default filtering should be used with {@link SpringBootApplication @SpringBootApplication}. By\n     * default no beans are included.\n     *\n     * @return if default filters should be used\n     * @see #includeFilters()\n     * @see #excludeFilters()\n     */\n    boolean useDefaultFilters() default true;\n\n    /**\n     * A set of include filters which can be used to add otherwise filtered beans to the application context.\n     *\n     * @return include filters to apply\n     */\n    Filter[] includeFilters() default {};\n\n    /**\n     * A set of exclude filters which can be used to filter beans that would otherwise be added to the application\n     * context.\n     *\n     * @return exclude filters to apply\n     */\n    Filter[] excludeFilters() default {};\n\n    /**\n     * Auto-configuration exclusions that should be applied for this test.\n     *\n     * @return auto-configuration exclusions to apply\n     */\n    @AliasFor(annotation = ImportAutoConfiguration.class, attribute = \"exclude\")\n    Class<?>[] excludeAutoConfiguration() default {};\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-test-autoconfigure/src/main/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTestContextBootstrapper.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.test.context.SpringBootTestContextBootstrapper;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\nimport org.springframework.test.context.TestContextBootstrapper;\n\n/**\n * {@link TestContextBootstrapper} for {@link MybatisPlusTest @MybatisTest} support.\n *\n * @author miemie\n * @since 2020-05-27\n */\nclass MybatisPlusTestContextBootstrapper extends SpringBootTestContextBootstrapper {\n\n    @Override\n    protected String[] getProperties(Class<?> testClass) {\n        MybatisPlusTest annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, MybatisPlusTest.class);\n        return (annotation != null) ? annotation.properties() : null;\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot-test-autoconfigure/src/main/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTypeExcludeFilter.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.context.TypeExcludeFilter;\nimport org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.core.annotation.AnnotatedElementUtils;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * {@link TypeExcludeFilter} for {@link MybatisPlusTest @MybatisPlusTest}.\n *\n * @author miemie\n * @since 2020-05-27\n */\nclass MybatisPlusTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {\n    private final MybatisPlusTest annotation;\n\n    MybatisPlusTypeExcludeFilter(Class<?> testClass) {\n        this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass, MybatisPlusTest.class);\n    }\n\n    @Override\n    protected boolean hasAnnotation() {\n        return this.annotation != null;\n    }\n\n    @Override\n    protected ComponentScan.Filter[] getFilters(FilterType type) {\n        switch (type) {\n            case INCLUDE:\n                return this.annotation.includeFilters();\n            case EXCLUDE:\n                return this.annotation.excludeFilters();\n            default:\n                throw new IllegalStateException(\"Unsupported type \" + type);\n        }\n    }\n\n    @Override\n    protected boolean isUseDefaultFilters() {\n        return this.annotation.useDefaultFilters();\n    }\n\n    @Override\n    protected Set<Class<?>> getDefaultIncludes() {\n        return Collections.emptySet();\n    }\n\n    @Override\n    protected Set<Class<?>> getComponentIncludes() {\n        return Collections.emptySet();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/build.gradle",
    "content": "compileJava {\n    options.release = 17\n}\n\ndependencies {\n    api project(\":mybatis-plus\")\n    api \"org.mybatis:mybatis-spring:3.0.5\"\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBoot3Version}\")\n    annotationProcessor \"org.springframework.boot:spring-boot-autoconfigure-processor:${springBoot3Version}\"\n    annotationProcessor \"org.springframework.boot:spring-boot-configuration-processor:${springBoot3Version}\"\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-jdbc\"\n    implementation \"org.springframework.boot:spring-boot-configuration-processor\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure-processor\"\n    implementation \"${lib['mybatis-thymeleaf']}\"\n    implementation \"org.mybatis.scripting:mybatis-velocity:2.3.0\"\n    implementation \"${lib.'mybatis-freemarker'}\"\n    implementation \"org.springframework.cloud:spring-cloud-commons:4.1.6\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test\"\n    testImplementation \"org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.5\"\n    testImplementation \"${lib.h2}\"\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.ISqlInjector;\nimport com.baomidou.mybatisplus.extension.spring.MybatisPlusApplicationContextAware;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.mapper.MapperFactoryBean;\nimport org.mybatis.spring.mapper.MapperScannerConfigurer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.beans.BeanWrapperImpl;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.boot.autoconfigure.AutoConfigurationPackages;\nimport org.springframework.boot.autoconfigure.AutoConfigureAfter;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.EnvironmentAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.env.Environment;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\nimport javax.sql.DataSource;\nimport java.beans.PropertyDescriptor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a\n * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.\n * <p>\n * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a\n * configuration file is specified as a property, those will be considered,\n * otherwise this auto-configuration will attempt to register mappers based on\n * the interface definitions in or under the root auto-configuration package.\n * </p>\n * <p> copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}</p>\n *\n * @author Eddú Meléndez\n * @author Josh Long\n * @author Kazuki Shimizu\n * @author Eduardo Macarrón\n */\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})\n@ConditionalOnSingleCandidate(DataSource.class)\n@EnableConfigurationProperties(MybatisPlusProperties.class)\n@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})\npublic class MybatisPlusAutoConfiguration implements InitializingBean {\n\n    private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);\n\n    private final MybatisPlusProperties properties;\n\n    private final Interceptor[] interceptors;\n\n    private final TypeHandler[] typeHandlers;\n\n    private final LanguageDriver[] languageDrivers;\n\n    private final ResourceLoader resourceLoader;\n\n    private final DatabaseIdProvider databaseIdProvider;\n\n    private final List<ConfigurationCustomizer> configurationCustomizers;\n\n    private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;\n\n    private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;\n\n    private final ApplicationContext applicationContext;\n\n    public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,\n                                        ObjectProvider<Interceptor[]> interceptorsProvider,\n                                        ObjectProvider<TypeHandler[]> typeHandlersProvider,\n                                        ObjectProvider<LanguageDriver[]> languageDriversProvider,\n                                        ResourceLoader resourceLoader,\n                                        ObjectProvider<DatabaseIdProvider> databaseIdProvider,\n                                        ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,\n                                        ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers,\n                                        ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,\n                                        ApplicationContext applicationContext) {\n        this.properties = properties;\n        this.interceptors = interceptorsProvider.getIfAvailable();\n        this.typeHandlers = typeHandlersProvider.getIfAvailable();\n        this.languageDrivers = languageDriversProvider.getIfAvailable();\n        this.resourceLoader = resourceLoader;\n        this.databaseIdProvider = databaseIdProvider.getIfAvailable();\n        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();\n        this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();\n        this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();\n        this.applicationContext = applicationContext;\n    }\n\n    @Override\n    public void afterPropertiesSet() {\n        if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) {\n            mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));\n        }\n        checkConfigFileExists();\n    }\n\n    private void checkConfigFileExists() {\n        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {\n            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());\n            Assert.state(resource.exists(),\n                \"Cannot find config location: \" + resource + \" (please add config file or check your Mybatis configuration)\");\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();\n        factory.setDataSource(dataSource);\n        factory.setVfs(SpringBootVFS.class);\n        factory.setApplicationContext(this.applicationContext);\n        if (StringUtils.hasText(this.properties.getConfigLocation())) {\n            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));\n        }\n        applyConfiguration(factory);\n        if (this.properties.getConfigurationProperties() != null) {\n            factory.setConfigurationProperties(this.properties.getConfigurationProperties());\n        }\n        if (!ObjectUtils.isEmpty(this.interceptors)) {\n            factory.setPlugins(this.interceptors);\n        }\n        if (this.databaseIdProvider != null) {\n            factory.setDatabaseIdProvider(this.databaseIdProvider);\n        }\n        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {\n            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());\n        }\n        if (this.properties.getTypeAliasesSuperType() != null) {\n            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());\n        }\n        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {\n            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());\n        }\n        if (!ObjectUtils.isEmpty(this.typeHandlers)) {\n            factory.setTypeHandlers(this.typeHandlers);\n        }\n        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {\n            factory.setMapperLocations(this.properties.resolveMapperLocations());\n        }\n        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);\n\n        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();\n        if (!ObjectUtils.isEmpty(this.languageDrivers)) {\n            factory.setScriptingLanguageDrivers(this.languageDrivers);\n        }\n        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);\n\n        applySqlSessionFactoryBeanCustomizers(factory);\n\n        GlobalConfig globalConfig = this.properties.getGlobalConfig();\n        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);\n        this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler);\n        this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler);\n        this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));\n        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);\n        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);\n        factory.setGlobalConfig(globalConfig);\n        return factory.getObject();\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            consumer.accept(this.applicationContext.getBean(clazz));\n        }\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeansThen(Class<T> clazz, Consumer<List<T>> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            final Map<String, T> beansOfType = this.applicationContext.getBeansOfType(clazz);\n            List<T> clazzList = new ArrayList<>();\n            beansOfType.forEach((k, v) -> clazzList.add(v));\n            consumer.accept(clazzList);\n        }\n    }\n\n    private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {\n        MybatisPlusProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();\n        MybatisConfiguration configuration = null;\n        if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) {\n            configuration = new MybatisConfiguration();\n        }\n        if (configuration != null && coreConfiguration != null) {\n            coreConfiguration.applyTo(configuration);\n        }\n        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {\n            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {\n                customizer.customize(configuration);\n            }\n        }\n        factory.setConfiguration(configuration);\n    }\n\n    private void applySqlSessionFactoryBeanCustomizers(MybatisSqlSessionFactoryBean factory) {\n        if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {\n            for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {\n                customizer.customize(factory);\n            }\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {\n        ExecutorType executorType = this.properties.getExecutorType();\n        if (executorType != null) {\n            return new SqlSessionTemplate(sqlSessionFactory, executorType);\n        } else {\n            return new SqlSessionTemplate(sqlSessionFactory);\n        }\n    }\n\n    /**\n     * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use\n     * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,\n     * similar to using Spring Data JPA repositories.\n     */\n    public static class AutoConfiguredMapperScannerRegistrar\n        implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {\n\n        private BeanFactory beanFactory;\n        private Environment environment;\n\n        @Override\n        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\n            if (!AutoConfigurationPackages.has(this.beanFactory)) {\n                logger.debug(\"Could not determine auto-configuration package, automatic mapper scanning disabled.\");\n                return;\n            }\n\n            logger.debug(\"Searching for mappers annotated with @Mapper\");\n\n            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);\n            if (logger.isDebugEnabled()) {\n                packages.forEach(pkg -> logger.debug(\"Using auto-configuration base package '{}'\", pkg));\n            }\n\n            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);\n            builder.addPropertyValue(\"processPropertyPlaceHolders\", true);\n            builder.addPropertyValue(\"annotationClass\", Mapper.class);\n            builder.addPropertyValue(\"basePackage\", StringUtils.collectionToCommaDelimitedString(packages));\n            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);\n            Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)\n                .collect(Collectors.toSet());\n            if (propertyNames.contains(\"lazyInitialization\")) {\n                // Need to mybatis-spring 2.0.2+\n                builder.addPropertyValue(\"lazyInitialization\", \"${mybatis-plus.lazy-initialization:${mybatis.lazy-initialization:false}}\");\n            }\n            if (propertyNames.contains(\"defaultScope\")) {\n                // Need to mybatis-spring 2.0.6+\n                builder.addPropertyValue(\"defaultScope\", \"${mybatis-plus.mapper-default-scope:}\");\n            }\n\n            // for spring-native\n            Boolean injectSqlSession = environment.getProperty(\"mybatis-plus.inject-sql-session-on-mapper-scan\", Boolean.class);\n            if (injectSqlSession == null) {\n                injectSqlSession = environment.getProperty(\"mybatis.inject-sql-session-on-mapper-scan\", Boolean.class, Boolean.TRUE);\n            }\n            if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {\n                ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;\n                Optional<String> sqlSessionTemplateBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));\n                Optional<String> sqlSessionFactoryBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));\n                if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {\n                    builder.addPropertyValue(\"sqlSessionTemplateBeanName\",\n                        sqlSessionTemplateBeanName.orElse(\"sqlSessionTemplate\"));\n                } else {\n                    builder.addPropertyValue(\"sqlSessionFactoryBeanName\", sqlSessionFactoryBeanName.get());\n                }\n            }\n            builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\n            registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());\n        }\n\n        @Override\n        public void setBeanFactory(BeanFactory beanFactory) {\n            this.beanFactory = beanFactory;\n        }\n\n        @Override\n        public void setEnvironment(Environment environment) {\n            this.environment = environment;\n        }\n\n        private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {\n            String[] beanNames = factory.getBeanNamesForType(type);\n            return beanNames.length > 0 ? beanNames[0] : null;\n        }\n    }\n\n    /**\n     * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan\n     * mappers based on the same component-scanning path as Spring Boot itself.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @Import(AutoConfiguredMapperScannerRegistrar.class)\n    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})\n    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {\n\n        @Override\n        public void afterPropertiesSet() {\n            logger.debug(\n                \"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.\");\n        }\n    }\n\n    /**\n     * @deprecated 3.5.13\n     * @see MybatisSqlSessionFactoryBean#setApplicationContext(ApplicationContext)\n     * @return MybatisPlusApplicationContextAware\n     */\n    @Bean\n    @Deprecated\n    @ConditionalOnMissingBean(MybatisPlusApplicationContextAware.class)\n    public MybatisPlusApplicationContextAware mybatisPlusSpringApplicationContextAware() {\n        return new MybatisPlusApplicationContextAware();\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json",
    "content": "{\n    \"groups\": [\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.configuration\",\n            \"sourceMethod\": \"getConfiguration()\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.global-config\",\n            \"sourceMethod\": \"getGlobalConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config\",\n            \"sourceMethod\": \"getDbConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\"\n        }\n    ],\n    \"properties\": [\n        {\n            \"defaultValue\": false,\n            \"name\": \"mybatis-plus.lazy-initialization\",\n            \"description\": \"Set whether enable lazy initialization for mapper bean.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"defaultValue\": \"\",\n            \"name\": \"mybatis-plus.mapper-default-scope\",\n            \"description\": \"A default scope for mapper bean that scanned by auto-configure.\",\n            \"type\": \"java.lang.String\"\n        },\n        {\n            \"defaultValue\": true,\n            \"name\": \"mybatis-plus.inject-sql-session-on-mapper-scan\",\n            \"description\": \"Set whether inject a SqlSessionTemplate or SqlSessionFactory bean (If you want to back to the behavior of 2.2.1 or before, specify false). If you use together with spring-native, should be set true.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.identifier-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.meta-object-handler\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.sql-injector\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config.key-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.default-scripting-language-driver\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-scripting-language\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"设置无效.\",\n                \"replacement\": \"mybatis-plus.configuration.default-scripting-language-driver\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-enum-type-handler\",\n            \"defaultValue\": \"org.apache.ibatis.type.EnumTypeHandler\",\n            \"description\": \"A default TypeHandler class for Enum.\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.type.TypeHandler>\"\n        }\n    ]\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/main/resources/META-INF/spring.factories",
    "content": "# Auto Configure\norg.springframework.boot.env.EnvironmentPostProcessor=\\\n  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n# Depends On Database Initialization Detectors\norg.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisDependsOnDatabaseInitializationDetector\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/autoconfigure/ApplicationTestStartSuccess.java",
    "content": "package com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2022-11-29\n */\n@SpringBootApplication\npublic class ApplicationTestStartSuccess {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = SpringApplication.run(ApplicationTestStartSuccess.class, args);\n        SqlSessionFactory bean = context.getBean(SqlSessionFactory.class);\n        Configuration configuration = bean.getConfiguration();\n        GlobalConfig config = GlobalConfigUtils.getGlobalConfig(configuration);\n        assertThat(config.getIdentifierGenerator()).isNotNull();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/MetadataTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.core.io.FileSystemResource;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.util.Properties;\n\n/**\n * 检查元数据生成\n *\n * @author nieqiurong 2019/2/9.\n */\nclass MetadataTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Metadata {\n        private String name;\n        private String type;\n        private String sourceType;\n    }\n\n    @Test\n    void checkSpringAutoconfigureMetadataProperties() throws IOException {\n        Properties properties = new Properties();\n        properties.load(new FileSystemResource(\"build/classes/java/main/META-INF/spring-autoconfigure-metadata.properties\").getInputStream());\n        Assertions.assertEquals(DataSourceAutoConfiguration.class.getName() + \",\" + MybatisPlusLanguageDriverAutoConfiguration.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".AutoConfigureAfter\"));\n        Assertions.assertEquals(DataSource.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnSingleCandidate\"));\n        Assertions.assertEquals(SqlSessionFactory.class.getName() + \",\" + SqlSessionFactoryBean.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnClass\"));\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/MybatisPlusPropertiesTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 属性测试\n *\n * @author nieqiurong 2019/5/4.\n */\nclass MybatisPlusPropertiesTest {\n\n    @Test\n    void resolveMapperLocationsTest() {\n        MybatisPlusProperties mybatisPlusProperties = new MybatisPlusProperties();\n        //默认扫描 classpath*:/mapper/**/*.xml\n        Assertions.assertEquals(\"classpath*:/mapper/**/*.xml\", mybatisPlusProperties.getMapperLocations()[0]);\n        Assertions.assertEquals(2, mybatisPlusProperties.resolveMapperLocations().length);\n        //扫描不存在的路径\n        mybatisPlusProperties.setMapperLocations(new String[]{\"classpath:mybatis-plus/*.xml\"});\n        Assertions.assertEquals(0, mybatisPlusProperties.resolveMapperLocations().length);\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/entity/Test.java",
    "content": "package com.baomidou.mybatisplus.test.entity;\n\nimport lombok.Data;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\n@Data\npublic class Test {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/mapper/TestMapper.java",
    "content": "package com.baomidou.mybatisplus.test.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.entity.Test;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\npublic interface TestMapper extends BaseMapper<Test> {\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private String version;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\")) {\n            JerryParser jerryParser = Jerry.create(new LagartoDOMBuilder().enableXmlMode());\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), $this.s(\"version\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency mp = dependenciesMap.get(\"mybatis-plus\");\n            Assertions.assertEquals(\"compile\", mp.getScope());\n            Assertions.assertFalse(mp.isOptional());\n            Dependency autoconfigure = dependenciesMap.get(\"spring-boot-autoconfigure\");\n            Assertions.assertEquals(\"compile\", autoconfigure.getScope());\n            Assertions.assertFalse(autoconfigure.isOptional());\n            Dependency jdbc = dependenciesMap.get(\"spring-boot-starter-jdbc\");\n            Assertions.assertEquals(\"compile\", jdbc.getScope());\n            Assertions.assertFalse(jdbc.isOptional());\n            Dependency configurationProcessor = dependenciesMap.get(\"spring-boot-configuration-processor\");\n            Assertions.assertEquals(\"compile\", configurationProcessor.getScope());\n            Assertions.assertTrue(configurationProcessor.isOptional());\n            Dependency autoconfigureProcessor = dependenciesMap.get(\"spring-boot-autoconfigure-processor\");\n            Assertions.assertEquals(\"compile\", autoconfigureProcessor.getScope());\n            Assertions.assertTrue(autoconfigureProcessor.isOptional());\n            Dependency bom = dependenciesMap.get(\"spring-boot-dependencies\");\n            Assertions.assertEquals(\"import\", bom.getScope());\n            Assertions.assertFalse(bom.isOptional());\n            Assertions.assertEquals(\"4.1.6\", dependenciesMap.get(\"spring-cloud-commons\").getVersion());\n            Assertions.assertEquals(\"3.0.5\", dependenciesMap.get(\"mybatis-spring\").getVersion());\n            Assertions.assertEquals(\"3.5.9\", dependenciesMap.get(\"spring-boot-dependencies\").getVersion());\n        }\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/resources/application.yml",
    "content": "spring:\n    datasource:\n        driver-class-name: org.h2.Driver\n        url: jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\n        username: sa\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/resources/mapper/modulea/testa.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter/src/test/resources/mapper/moduleb/testb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/build.gradle",
    "content": "compileJava {\n    options.release = 17\n}\n\ndependencies {\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot3-starter\")\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-test-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBoot3Version}\")\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-test\"\n    api \"org.springframework:spring-tx\"\n    testImplementation \"${lib.h2}\"\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/main/resources/META-INF/spring/com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports",
    "content": "# AutoConfigureMybatis auto-configuration imports\norg.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration\norg.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration\norg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/main/resources/META-INF/spring.factories",
    "content": "# AutoConfigureMybatis auto-configuration imports\ncom.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus=\\\norg.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\\\norg.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\\\norg.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\\\norg.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\\\norg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusSampleTest.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n//@MybatisPlusTest(properties = \"spring.datasource.schema=classpath:schema.sql\")\n@MybatisPlusTest\nclass MybatisPlusSampleTest {\n\n    @Autowired\n    private SampleMapper sampleMapper;\n\n    @Test\n    void testInsert() {\n        Sample sample = new Sample();\n        sampleMapper.insert(sample);\n        assertThat(sample.getId()).isNotNull();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTestApplication.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@SpringBootApplication\npublic class MybatisPlusTestApplication {\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/Sample.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Data\npublic class Sample {\n    private Long id;\n    private String name;\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/SampleMapper.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Mapper\npublic interface SampleMapper extends BaseMapper<Sample> {\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/resources/application.yml",
    "content": "spring:\n    datasource:\n        schema: classpath:schema.sql\n\nlogging:\n    level:\n        org.springframework.jdbc: debug\n        com.baomidou.mybatisplus.test.autoconfigure: debug\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot3-starter-test/src/test/resources/schema.sql",
    "content": "drop table if exists sample;\ncreate table if not exists sample\n(\n    id   bigint,\n    name varchar\n);\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/build.gradle",
    "content": "compileJava {\n    options.release = 17\n}\n\ndependencies {\n    api project(\":mybatis-plus\")\n    api \"org.mybatis:mybatis-spring:4.0.0\"\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBoot4Version}\")\n    annotationProcessor \"org.springframework.boot:spring-boot-autoconfigure-processor:${springBoot4Version}\"\n    annotationProcessor \"org.springframework.boot:spring-boot-configuration-processor:${springBoot4Version}\"\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-jdbc\"\n    implementation \"org.springframework.boot:spring-boot-configuration-processor\"\n    implementation \"org.springframework.boot:spring-boot-autoconfigure-processor\"\n    implementation \"${lib['mybatis-thymeleaf']}\"\n    implementation \"org.mybatis.scripting:mybatis-velocity:2.3.0\"\n    implementation \"${lib.'mybatis-freemarker'}\"\n    implementation \"org.springframework.cloud:spring-cloud-commons:5.0.0\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test\"\n    testImplementation \"org.mybatis.spring.boot:mybatis-spring-boot-starter:4.0.1\"\n    testImplementation \"${lib.h2}\"\n}\n\ncompileJava.dependsOn(processResources)\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java",
    "content": "/*\n * Copyright (c) 2011-2025, baomidou (jobob@qq.com).\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.baomidou.mybatisplus.autoconfigure;\n\n\nimport com.baomidou.mybatisplus.core.MybatisConfiguration;\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.handlers.AnnotationHandler;\nimport com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;\nimport com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;\nimport com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;\nimport com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;\nimport com.baomidou.mybatisplus.core.injector.ISqlInjector;\nimport com.baomidou.mybatisplus.extension.spring.MybatisPlusApplicationContextAware;\nimport com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.mapping.DatabaseIdProvider;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.scripting.LanguageDriver;\nimport org.apache.ibatis.session.ExecutorType;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.transaction.TransactionFactory;\nimport org.apache.ibatis.type.TypeHandler;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.mapper.MapperFactoryBean;\nimport org.mybatis.spring.mapper.MapperScannerConfigurer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeanWrapper;\nimport org.springframework.beans.BeanWrapperImpl;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.ListableBeanFactory;\nimport org.springframework.beans.factory.ObjectProvider;\nimport org.springframework.beans.factory.config.BeanDefinition;\nimport org.springframework.beans.factory.support.BeanDefinitionBuilder;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.boot.autoconfigure.AutoConfigurationPackages;\nimport org.springframework.boot.autoconfigure.AutoConfigureAfter;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.EnvironmentAware;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.env.Environment;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.util.ObjectUtils;\nimport org.springframework.util.StringUtils;\n\nimport javax.sql.DataSource;\nimport java.beans.PropertyDescriptor;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a\n * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.\n * <p>\n * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a\n * configuration file is specified as a property, those will be considered,\n * otherwise this auto-configuration will attempt to register mappers based on\n * the interface definitions in or under the root auto-configuration package.\n * </p>\n * <p> copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}</p>\n *\n * @author Eddú Meléndez\n * @author Josh Long\n * @author Kazuki Shimizu\n * @author Eduardo Macarrón\n */\n@Configuration(proxyBeanMethods = false)\n@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})\n@ConditionalOnSingleCandidate(DataSource.class)\n@EnableConfigurationProperties(MybatisPlusProperties.class)\n@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})\npublic class MybatisPlusAutoConfiguration implements InitializingBean {\n\n    private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);\n\n    private final MybatisPlusProperties properties;\n\n    private final Interceptor[] interceptors;\n\n    private final TypeHandler[] typeHandlers;\n\n    private final LanguageDriver[] languageDrivers;\n\n    private final ResourceLoader resourceLoader;\n\n    private final DatabaseIdProvider databaseIdProvider;\n\n    private final List<ConfigurationCustomizer> configurationCustomizers;\n\n    private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers;\n\n    private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers;\n\n    private final ApplicationContext applicationContext;\n\n    public MybatisPlusAutoConfiguration(MybatisPlusProperties properties,\n                                        ObjectProvider<Interceptor[]> interceptorsProvider,\n                                        ObjectProvider<TypeHandler[]> typeHandlersProvider,\n                                        ObjectProvider<LanguageDriver[]> languageDriversProvider,\n                                        ResourceLoader resourceLoader,\n                                        ObjectProvider<DatabaseIdProvider> databaseIdProvider,\n                                        ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,\n                                        ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers,\n                                        ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider,\n                                        ApplicationContext applicationContext) {\n        this.properties = properties;\n        this.interceptors = interceptorsProvider.getIfAvailable();\n        this.typeHandlers = typeHandlersProvider.getIfAvailable();\n        this.languageDrivers = languageDriversProvider.getIfAvailable();\n        this.resourceLoader = resourceLoader;\n        this.databaseIdProvider = databaseIdProvider.getIfAvailable();\n        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();\n        this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();\n        this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable();\n        this.applicationContext = applicationContext;\n    }\n\n    @Override\n    public void afterPropertiesSet() {\n        if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) {\n            mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties));\n        }\n        checkConfigFileExists();\n    }\n\n    private void checkConfigFileExists() {\n        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {\n            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());\n            Assert.state(resource.exists(),\n                \"Cannot find config location: \" + resource + \" (please add config file or check your Mybatis configuration)\");\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {\n        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();\n        factory.setDataSource(dataSource);\n        factory.setVfs(SpringBootVFS.class);\n        factory.setApplicationContext(this.applicationContext);\n        if (StringUtils.hasText(this.properties.getConfigLocation())) {\n            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));\n        }\n        applyConfiguration(factory);\n        if (this.properties.getConfigurationProperties() != null) {\n            factory.setConfigurationProperties(this.properties.getConfigurationProperties());\n        }\n        if (!ObjectUtils.isEmpty(this.interceptors)) {\n            factory.setPlugins(this.interceptors);\n        }\n        if (this.databaseIdProvider != null) {\n            factory.setDatabaseIdProvider(this.databaseIdProvider);\n        }\n        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {\n            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());\n        }\n        if (this.properties.getTypeAliasesSuperType() != null) {\n            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());\n        }\n        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {\n            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());\n        }\n        if (!ObjectUtils.isEmpty(this.typeHandlers)) {\n            factory.setTypeHandlers(this.typeHandlers);\n        }\n        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {\n            factory.setMapperLocations(this.properties.resolveMapperLocations());\n        }\n        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);\n\n        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();\n        if (!ObjectUtils.isEmpty(this.languageDrivers)) {\n            factory.setScriptingLanguageDrivers(this.languageDrivers);\n        }\n        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);\n\n        applySqlSessionFactoryBeanCustomizers(factory);\n\n        GlobalConfig globalConfig = this.properties.getGlobalConfig();\n        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);\n        this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler);\n        this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler);\n        this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i));\n        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);\n        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);\n        factory.setGlobalConfig(globalConfig);\n        return factory.getObject();\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            consumer.accept(this.applicationContext.getBean(clazz));\n        }\n    }\n\n    /**\n     * 检查spring容器里是否有对应的bean,有则进行消费\n     *\n     * @param clazz    class\n     * @param consumer 消费\n     * @param <T>      泛型\n     */\n    private <T> void getBeansThen(Class<T> clazz, Consumer<List<T>> consumer) {\n        if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {\n            final Map<String, T> beansOfType = this.applicationContext.getBeansOfType(clazz);\n            List<T> clazzList = new ArrayList<>();\n            beansOfType.forEach((k, v) -> clazzList.add(v));\n            consumer.accept(clazzList);\n        }\n    }\n\n    private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {\n        MybatisPlusProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration();\n        MybatisConfiguration configuration = null;\n        if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) {\n            configuration = new MybatisConfiguration();\n        }\n        if (configuration != null && coreConfiguration != null) {\n            coreConfiguration.applyTo(configuration);\n        }\n        if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {\n            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {\n                customizer.customize(configuration);\n            }\n        }\n        factory.setConfiguration(configuration);\n    }\n\n    private void applySqlSessionFactoryBeanCustomizers(MybatisSqlSessionFactoryBean factory) {\n        if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) {\n            for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) {\n                customizer.customize(factory);\n            }\n        }\n    }\n\n    @Bean\n    @ConditionalOnMissingBean\n    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {\n        ExecutorType executorType = this.properties.getExecutorType();\n        if (executorType != null) {\n            return new SqlSessionTemplate(sqlSessionFactory, executorType);\n        } else {\n            return new SqlSessionTemplate(sqlSessionFactory);\n        }\n    }\n\n    /**\n     * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use\n     * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,\n     * similar to using Spring Data JPA repositories.\n     */\n    public static class AutoConfiguredMapperScannerRegistrar\n        implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {\n\n        private BeanFactory beanFactory;\n        private Environment environment;\n\n        @Override\n        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n\n            if (!AutoConfigurationPackages.has(this.beanFactory)) {\n                logger.debug(\"Could not determine auto-configuration package, automatic mapper scanning disabled.\");\n                return;\n            }\n\n            logger.debug(\"Searching for mappers annotated with @Mapper\");\n\n            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);\n            if (logger.isDebugEnabled()) {\n                packages.forEach(pkg -> logger.debug(\"Using auto-configuration base package '{}'\", pkg));\n            }\n\n            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);\n            builder.addPropertyValue(\"processPropertyPlaceHolders\", true);\n            builder.addPropertyValue(\"annotationClass\", Mapper.class);\n            builder.addPropertyValue(\"basePackage\", StringUtils.collectionToCommaDelimitedString(packages));\n            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);\n            Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)\n                .collect(Collectors.toSet());\n            if (propertyNames.contains(\"lazyInitialization\")) {\n                // Need to mybatis-spring 2.0.2+\n                builder.addPropertyValue(\"lazyInitialization\", \"${mybatis-plus.lazy-initialization:${mybatis.lazy-initialization:false}}\");\n            }\n            if (propertyNames.contains(\"defaultScope\")) {\n                // Need to mybatis-spring 2.0.6+\n                builder.addPropertyValue(\"defaultScope\", \"${mybatis-plus.mapper-default-scope:}\");\n            }\n\n            // for spring-native\n            Boolean injectSqlSession = environment.getProperty(\"mybatis-plus.inject-sql-session-on-mapper-scan\", Boolean.class);\n            if (injectSqlSession == null) {\n                injectSqlSession = environment.getProperty(\"mybatis.inject-sql-session-on-mapper-scan\", Boolean.class, Boolean.TRUE);\n            }\n            if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {\n                ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;\n                Optional<String> sqlSessionTemplateBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));\n                Optional<String> sqlSessionFactoryBeanName = Optional\n                    .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));\n                if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {\n                    builder.addPropertyValue(\"sqlSessionTemplateBeanName\",\n                        sqlSessionTemplateBeanName.orElse(\"sqlSessionTemplate\"));\n                } else {\n                    builder.addPropertyValue(\"sqlSessionFactoryBeanName\", sqlSessionFactoryBeanName.get());\n                }\n            }\n            builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);\n\n            registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());\n        }\n\n        @Override\n        public void setBeanFactory(BeanFactory beanFactory) {\n            this.beanFactory = beanFactory;\n        }\n\n        @Override\n        public void setEnvironment(Environment environment) {\n            this.environment = environment;\n        }\n\n        private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {\n            String[] beanNames = factory.getBeanNamesForType(type);\n            return beanNames.length > 0 ? beanNames[0] : null;\n        }\n    }\n\n    /**\n     * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan\n     * mappers based on the same component-scanning path as Spring Boot itself.\n     */\n    @Configuration(proxyBeanMethods = false)\n    @Import(AutoConfiguredMapperScannerRegistrar.class)\n    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})\n    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {\n\n        @Override\n        public void afterPropertiesSet() {\n            logger.debug(\n                \"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.\");\n        }\n    }\n\n    /**\n     * @deprecated 3.5.13\n     * @see MybatisSqlSessionFactoryBean#setApplicationContext(ApplicationContext)\n     * @return MybatisPlusApplicationContextAware\n     */\n    @Bean\n    @Deprecated\n    @ConditionalOnMissingBean(MybatisPlusApplicationContextAware.class)\n    public MybatisPlusApplicationContextAware mybatisPlusSpringApplicationContextAware() {\n        return new MybatisPlusApplicationContextAware();\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json",
    "content": "{\n    \"groups\": [\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.configuration\",\n            \"sourceMethod\": \"getConfiguration()\",\n            \"type\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.global-config\",\n            \"sourceMethod\": \"getGlobalConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config\",\n            \"sourceMethod\": \"getDbConfig()\",\n            \"type\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\"\n        }\n    ],\n    \"properties\": [\n        {\n            \"defaultValue\": false,\n            \"name\": \"mybatis-plus.lazy-initialization\",\n            \"description\": \"Set whether enable lazy initialization for mapper bean.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"defaultValue\": \"\",\n            \"name\": \"mybatis-plus.mapper-default-scope\",\n            \"description\": \"A default scope for mapper bean that scanned by auto-configure.\",\n            \"type\": \"java.lang.String\"\n        },\n        {\n            \"defaultValue\": true,\n            \"name\": \"mybatis-plus.inject-sql-session-on-mapper-scan\",\n            \"description\": \"Set whether inject a SqlSessionTemplate or SqlSessionFactory bean (If you want to back to the behavior of 2.2.1 or before, specify false). If you use together with spring-native, should be set true.\",\n            \"type\": \"java.lang.Boolean\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.identifier-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.meta-object-handler\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig\",\n            \"name\": \"mybatis-plus.global-config.sql-injector\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig\",\n            \"name\": \"mybatis-plus.global-config.db-config.key-generator\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"3.0开始废除此属性，请使用@Bean的方式注入至Spring容器.\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties\",\n            \"name\": \"mybatis-plus.default-scripting-language-driver\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\"\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-scripting-language\",\n            \"defaultValue\": \"com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.scripting.LanguageDriver>\",\n            \"deprecation\": {\n                \"level\": \"error\",\n                \"reason\": \"设置无效.\",\n                \"replacement\": \"mybatis-plus.configuration.default-scripting-language-driver\"\n            }\n        },\n        {\n            \"sourceType\": \"com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties$CoreConfiguration\",\n            \"name\": \"mybatis-plus.configuration.default-enum-type-handler\",\n            \"defaultValue\": \"org.apache.ibatis.type.EnumTypeHandler\",\n            \"description\": \"A default TypeHandler class for Enum.\",\n            \"type\": \"java.lang.Class<? extends org.apache.ibatis.type.TypeHandler>\"\n        }\n    ]\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
    "content": "com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/main/resources/META-INF/spring.factories",
    "content": "# Auto Configure\norg.springframework.boot.env.EnvironmentPostProcessor=\\\n  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusInnerInterceptorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration,\\\n  com.baomidou.mybatisplus.autoconfigure.DdlAutoConfiguration\n# Depends On Database Initialization Detectors\norg.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisDependsOnDatabaseInitializationDetector\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/autoconfigure/ApplicationTestStartSuccess.java",
    "content": "package com.baomidou.mybatisplus.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.config.GlobalConfig;\nimport com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;\nimport org.apache.ibatis.session.Configuration;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2022-11-29\n */\n@SpringBootApplication\npublic class ApplicationTestStartSuccess {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = SpringApplication.run(ApplicationTestStartSuccess.class, args);\n        SqlSessionFactory bean = context.getBean(SqlSessionFactory.class);\n        Configuration configuration = bean.getConfiguration();\n        GlobalConfig config = GlobalConfigUtils.getGlobalConfig(configuration);\n        assertThat(config.getIdentifierGenerator()).isNotNull();\n    }\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/test/MetadataTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;\nimport org.springframework.core.io.FileSystemResource;\n\nimport javax.sql.DataSource;\nimport java.io.IOException;\nimport java.util.Properties;\n\n/**\n * 检查元数据生成\n *\n * @author nieqiurong 2019/2/9.\n */\nclass MetadataTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Metadata {\n        private String name;\n        private String type;\n        private String sourceType;\n    }\n\n    @Test\n    void checkSpringAutoconfigureMetadataProperties() throws IOException {\n        Properties properties = new Properties();\n        properties.load(new FileSystemResource(\"build/classes/java/main/META-INF/spring-autoconfigure-metadata.properties\").getInputStream());\n        Assertions.assertEquals(DataSourceAutoConfiguration.class.getName() + \",\" + MybatisPlusLanguageDriverAutoConfiguration.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".AutoConfigureAfter\"));\n        Assertions.assertEquals(DataSource.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnSingleCandidate\"));\n        Assertions.assertEquals(SqlSessionFactory.class.getName() + \",\" + SqlSessionFactoryBean.class.getName(), properties.getProperty(MybatisPlusAutoConfiguration.class.getName() + \".ConditionalOnClass\"));\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/test/MybatisPlusPropertiesTest.java",
    "content": "package com.baomidou.mybatisplus.test;\n\nimport com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\n/**\n * 属性测试\n *\n * @author nieqiurong 2019/5/4.\n */\nclass MybatisPlusPropertiesTest {\n\n    @Test\n    void resolveMapperLocationsTest() {\n        MybatisPlusProperties mybatisPlusProperties = new MybatisPlusProperties();\n        //默认扫描 classpath*:/mapper/**/*.xml\n        Assertions.assertEquals(\"classpath*:/mapper/**/*.xml\", mybatisPlusProperties.getMapperLocations()[0]);\n        Assertions.assertEquals(2, mybatisPlusProperties.resolveMapperLocations().length);\n        //扫描不存在的路径\n        mybatisPlusProperties.setMapperLocations(new String[]{\"classpath:mybatis-plus/*.xml\"});\n        Assertions.assertEquals(0, mybatisPlusProperties.resolveMapperLocations().length);\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/test/entity/Test.java",
    "content": "package com.baomidou.mybatisplus.test.entity;\n\nimport lombok.Data;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\n@Data\npublic class Test {\n\n    private Long id;\n\n    private String name;\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/test/mapper/TestMapper.java",
    "content": "package com.baomidou.mybatisplus.test.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.test.entity.Test;\n\n/**\n * @author nieqiurong 2019/5/4.\n */\npublic interface TestMapper extends BaseMapper<Test> {\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/java/com/baomidou/mybatisplus/test/pom/GeneratePomTest.java",
    "content": "package com.baomidou.mybatisplus.test.pom;\n\nimport jodd.io.FileUtil;\nimport jodd.jerry.Jerry;\nimport jodd.jerry.JerryParser;\nimport jodd.lagarto.dom.LagartoDOMBuilder;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 检查pom依赖\n *\n * @author nieqiurong 2019/2/9.\n */\nclass GeneratePomTest {\n\n    @Data\n    @AllArgsConstructor\n    private static class Dependency {\n        private String artifactId;\n        private String scope;\n        private String version;\n        private boolean optional;\n    }\n\n    @Test\n    void test() throws IOException {\n        try (InputStream inputStream = new FileInputStream(\"build/publications/mavenJava/pom-default.xml\")) {\n            JerryParser jerryParser = Jerry.create(new LagartoDOMBuilder().enableXmlMode());\n            Jerry doc = jerryParser.parse(FileUtil.readUTFString(inputStream));\n            Jerry dependencies = doc.s(\"dependencies dependency\");\n            Map<String, Dependency> dependenciesMap = new HashMap<>();\n            dependencies.forEach($this -> {\n                String artifactId = $this.s(\"artifactId\").text();\n                dependenciesMap.put(artifactId, new Dependency(artifactId, $this.s(\"scope\").text(), $this.s(\"version\").text(), Boolean.parseBoolean($this.s(\"optional\").text())));\n            });\n            Dependency mp = dependenciesMap.get(\"mybatis-plus\");\n            Assertions.assertEquals(\"compile\", mp.getScope());\n            Assertions.assertFalse(mp.isOptional());\n            Dependency autoconfigure = dependenciesMap.get(\"spring-boot-autoconfigure\");\n            Assertions.assertEquals(\"compile\", autoconfigure.getScope());\n            Assertions.assertFalse(autoconfigure.isOptional());\n            Dependency jdbc = dependenciesMap.get(\"spring-boot-starter-jdbc\");\n            Assertions.assertEquals(\"compile\", jdbc.getScope());\n            Assertions.assertFalse(jdbc.isOptional());\n            Dependency configurationProcessor = dependenciesMap.get(\"spring-boot-configuration-processor\");\n            Assertions.assertEquals(\"compile\", configurationProcessor.getScope());\n            Assertions.assertTrue(configurationProcessor.isOptional());\n            Dependency autoconfigureProcessor = dependenciesMap.get(\"spring-boot-autoconfigure-processor\");\n            Assertions.assertEquals(\"compile\", autoconfigureProcessor.getScope());\n            Assertions.assertTrue(autoconfigureProcessor.isOptional());\n            Dependency bom = dependenciesMap.get(\"spring-boot-dependencies\");\n            Assertions.assertEquals(\"import\", bom.getScope());\n            Assertions.assertFalse(bom.isOptional());\n            Assertions.assertEquals(\"5.0.0\", dependenciesMap.get(\"spring-cloud-commons\").getVersion());\n            Assertions.assertEquals(\"4.0.0\", dependenciesMap.get(\"mybatis-spring\").getVersion());\n            Assertions.assertEquals(\"4.0.1\", dependenciesMap.get(\"spring-boot-dependencies\").getVersion());\n        }\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/resources/application.yml",
    "content": "spring:\n    datasource:\n        driver-class-name: org.h2.Driver\n        url: jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\n        username: sa\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/resources/mapper/modulea/testa.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter/src/test/resources/mapper/moduleb/testb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.baomidou.mybatisplus.test.mapper.TestMapper\">\n\n</mapper>\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/build.gradle",
    "content": "compileJava {\n    options.release = 17\n}\n\ndependencies {\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot4-starter\")\n    api project(\":spring-boot-starter:mybatis-plus-spring-boot-test-autoconfigure\")\n    implementation platform(\"org.springframework.boot:spring-boot-dependencies:${springBoot4Version}\")\n    api \"org.springframework.boot:spring-boot-autoconfigure\"\n    api \"org.springframework.boot:spring-boot-starter-test\"\n    api \"org.springframework:spring-tx\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-jdbc\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-cache\"\n    testImplementation \"${lib.h2}\"\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/main/resources/META-INF/spring/com.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus.imports",
    "content": "# AutoConfigureMybatis auto-configuration imports\norg.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration\norg.springframework.boot.jdbc.autoconfigure.DataSourceInitializationAutoConfiguration\norg.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration\norg.springframework.boot.jdbc.autoconfigure.JdbcClientAutoConfiguration\norg.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration\norg.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration\noptional:org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration\noptional:org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration\noptional:org.springframework.boot.testcontainers.service.connection.ServiceConnectionAutoConfiguration\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/main/resources/META-INF/spring.factories",
    "content": "# AutoConfigureMybatis auto-configuration imports\ncom.baomidou.mybatisplus.test.autoconfigure.AutoConfigureMybatisPlus=\\\norg.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration,\\\norg.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration,\\\norg.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration,\\\norg.springframework.boot.jdbc.autoconfigure.JdbcTemplateAutoConfiguration,\\\norg.springframework.boot.liquibase.autoconfigure.LiquibaseAutoConfiguration,\\\norg.springframework.boot.transaction.autoconfigure.TransactionAutoConfiguration,\\\norg.springframework.boot.jdbc.autoconfigure.DataSourceInitializationAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\\\ncom.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration\n\n\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusSampleTest.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n//@MybatisPlusTest(properties = \"spring.datasource.schema=classpath:schema.sql\")\n@MybatisPlusTest\nclass MybatisPlusSampleTest {\n\n    @Autowired\n    private SampleMapper sampleMapper;\n\n    @Test\n    void testInsert() {\n        Sample sample = new Sample();\n        sampleMapper.insert(sample);\n        assertThat(sample.getId()).isNotNull();\n    }\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/MybatisPlusTestApplication.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@SpringBootApplication\npublic class MybatisPlusTestApplication {\n\n\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/Sample.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport lombok.Data;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Data\npublic class Sample {\n    private Long id;\n    private String name;\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/java/com/baomidou/mybatisplus/test/autoconfigure/SampleMapper.java",
    "content": "package com.baomidou.mybatisplus.test.autoconfigure;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * @author miemie\n * @since 2020-05-27\n */\n@Mapper\npublic interface SampleMapper extends BaseMapper<Sample> {\n}\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/resources/application.yml",
    "content": "spring:\n    sql:\n        init:\n            schema-locations: classpath:schema.sql\n    datasource:\n        driver-class-name: org.h2.Driver\n        url: jdbc:h2:mem:test;MODE=mysql;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE\n        username: sa\n\nlogging:\n    level:\n        org.springframework: debug\n        com.baomidou.mybatisplus.test.autoconfigure: debug\n"
  },
  {
    "path": "spring-boot-starter/mybatis-plus-spring-boot4-starter-test/src/test/resources/schema.sql",
    "content": "drop table if exists sample;\ncreate table if not exists sample\n(\n    id   bigint,\n    name varchar\n);\n"
  }
]