Smart MyBatis 3.0.2:数据库们终于被安排进了同一个会议室

郭胜凯2026/08/17公告方言版本更新

过去一段时间,Smart MyBatis 对数据库的态度非常朴素:

你只要长得像数据库,我就先按 MySQL 理解你。

这套沟通方式不能说完全没有效果。至少 MySQL 很满意。

但 Oracle、H2、DB2 显然有自己的想法。比如 Oracle 不喜欢表别名里的 AS,没有原生 BOOLEAN,分页时也不想看见 LIMIT。你如果坚持给它发 MySQL SQL,它不会和你争论,它只会冷静地给你一串 ORA 编号,让你在搜索引擎里完成剩余的人生思考。

所以从 Smart MyBatis 3.0.2 开始,我们正式加入了数据库方言机制。现在不同数据库终于可以说自己的语言,框架负责翻译,Service 层继续假装世界大同。

方言不是给 SQL 换个口音

这次不是简单写几个 if (database == ORACLE)

Smart MyBatis 把数据库差异统一收敛到了 SqlDialects 接口中。应用启动时,框架会读取 JDBC driver class,通过 DialectResolver 找到带 @SmartDialect 的实现,然后把它放进 SmartConfigHolder

后续所有通用 SQL 都从这里经过:

实体与注解
  -> MapperDeclaration
  -> BaseSqlProvider
  -> 当前 SqlDialects
  -> MyBatis 执行

方言负责的内容包括但不限于:

  • 表名、列名如何引用
  • 分页到底说 LIMIT 还是 FETCH FIRST
  • Java 类型映射成 INTNUMBERVARCHAR2 还是别的什么
  • AUTO 主键怎么建、怎么回填
  • 批量 INSERT 怎么写
  • CREATE TABLE 和 ALTER TABLE 怎么保持基本礼貌
  • 表别名能不能使用 AS
  • JSON 字段应该住进 LONGTEXT 还是 CLOB

换句话说,以前这些差异散落在 SQL 的各个角落,现在它们都有了固定工位,并且要求打卡。

当前到会数据库名单

3.0.2 内置支持以下方言:

数据库JDBC Driver会议表现
MySQLcom.mysql.cj.jdbc.Driver老员工,熟悉流程,偶尔坚持使用反引号
H2org.h2.Driver测试部门代表,启动快,大小写问题非常有原则
Oracleoracle.jdbc.OracleDriver规章制度完整,每个语法错误都有编号
DB2com.ibm.db2.jcc.DB2Driver发言不多,但 FETCH FIRST 必须写对

通常只需要正常配置数据源:

spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:smart_mybatis

Starter 会自动根据 spring.datasource.driver-class-name 选择方言。也可以显式指定:

spring:
  mybatis:
    smart:
      dialect-driver-class-name: org.h2.Driver

如果驱动没有匹配到,当前版本会回退到 MySQL 方言。这不是说所有数据库最终都会理解 MySQL,而是框架在无法判断时选择了最熟悉的答案。请务必通过启动日志确认最终方言,别让数据库在不知情的情况下被安排成 MySQL。

Oracle:这次没有让 Service 写检讨

Oracle 适配里最值得单独说的是批量插入和自增主键。

普通数据库通常可以用一条批量 INSERT,再通过 JDBC generated keys 回填主键。Oracle 的 identity 批量回填比较有自己的工作流程。如果让业务层为了它把 insertBatch 手工拆成多个 insert,代码当然也能跑,只是框架会显得像一个只负责提出要求的产品经理。

现在业务仍然这样写:

int rows = studentMapper.insertBatch(students);

Oracle 方言会告诉框架批量插入需要复用基础 insert。框架内部逐条执行、逐条回填 AUTO 主键,并累计影响行数。Service 不需要判断数据库类型,也不需要为了 Oracle 临时转行做 SQL 翻译。

框架适配数据库,业务表达业务。这个原则看起来普通,通常是在吃过亏以后才显得很有文化。

顺便修了一批 BUG

加入方言的过程,很像把房间里的灯全部打开。灯亮以后,首先看见的通常不是新家具,而是以前觉得“问题不大”的地方。

这次一起处理和补强的内容包括:

1. 只有 ORDER BY、没有 WHERE 时,排序不再假装没来

以前某些仅包含排序条件的 Where,因为没有普通过滤条件和 limit,会被提前判断为空,最后 SQL 中没有 ORDER BY。

现在排序条件会独立生效,并增加了回归测试。毕竟用户明确要求排序时,框架最基本的职业素养就是不要自由发挥。

2. 批量插入主键不再集体失忆

补强了批量插入的主键生成与回填流程,并针对不同数据库提供方言化处理。插入完成后,对象里的 ID 应该是数据库生成的主键,而不是一排整齐的 0。整齐很好,但不能用在这里。

3. LEFT JOIN 的别名和关联字段更加稳定

修复了关联查询中的别名处理,补充了 selectWithRelations,并支持嵌套 Where。现在关联字段可以参与查询和过滤,多表查询也不用靠“这个 _t 到底是谁”来锻炼记忆力。

4. 自动同步表结构开始尊重数据库差异

CREATE TABLE、ALTER TABLE、字段类型、主键定义和注释处理全部经过方言。修复了表名冲突、字段类型比较和不同数据库 ALTER 语法带来的问题。

auto-sync-db 依然更适合开发环境。它现在更聪明了,但还没有聪明到可以替你参加生产变更评审。

5. JSON 字段读写和泛型映射更完整

JSON 字段会根据方言选择 LONGTEXTCLOB,初始化器也会为查询结果注入对应的 JSON ResultMap。像 List<SomeType> 这样的泛型字段,不再被简单理解成“这是一个 List,里面的事情以后再说”。

6. 初始化脚本和原始 SQL 更经得起正常使用

初始化脚本会在表为空时执行;脚本拆分会识别字符串、注释、反引号和分号边界。字符串里的分号可以继续作为一个有正常职业的分号存在,不会被误判成 SQL 已经结束。

升级方式

Maven:

<dependency>
    <groupId>ink.icoding</groupId>
    <artifactId>spring-boot-starter-smart-mybatis</artifactId>
    <version>3.0.2</version>
</dependency>

Smart MyBatis 仍然是 MyBatis 的增强层,因此项目还需要正常引入官方 MyBatis Starter 和对应数据库驱动。

升级后建议至少验证:

  1. 启动日志中的方言是否正确。
  2. 单条和批量插入后主键是否回填。
  3. 分页、排序和关联查询生成的 SQL。
  4. JSON 与枚举字段的读写。
  5. 开启 auto-sync-db 时生成的 DDL。
  6. 项目里的原始 SQL 是否符合目标数据库语法。

原始 SQL 不会自动经过方言翻译。你亲手写的 SQL,框架会尊重你的创作自由,也会尊重数据库拒绝它的权利。

最后

支持方言以后,Smart MyBatis 并没有变成一个庞大的数据库翻译机构。核心思路仍然没变:

  • 实体和 Mapper 描述业务模型
  • Where 描述查询意图
  • SqlDialects 负责数据库差异
  • MyBatis 继续负责它本来就很擅长的执行工作

这次更新的目标不是让所有数据库看起来都像 MySQL,而是让它们保持各自的脾气,同时不把脾气传递给业务代码。

详细机制请阅读数据库方言机制,完整变更和限制见3.0.2 版本说明

欢迎升级。遇到问题可以提 Issue,请尽量附上数据库类型、驱动版本、生成 SQL 和完整异常。只发一句“不能用”也可以,只是我们双方都会获得一次猜谜体验。