Smart MyBatis 3.0.2:数据库们终于被安排进了同一个会议室
过去一段时间,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 类型映射成
INT、NUMBER、VARCHAR2还是别的什么 - AUTO 主键怎么建、怎么回填
- 批量 INSERT 怎么写
- CREATE TABLE 和 ALTER TABLE 怎么保持基本礼貌
- 表别名能不能使用
AS - JSON 字段应该住进
LONGTEXT还是CLOB
换句话说,以前这些差异散落在 SQL 的各个角落,现在它们都有了固定工位,并且要求打卡。
当前到会数据库名单
3.0.2 内置支持以下方言:
| 数据库 | JDBC Driver | 会议表现 |
|---|---|---|
| MySQL | com.mysql.cj.jdbc.Driver | 老员工,熟悉流程,偶尔坚持使用反引号 |
| H2 | org.h2.Driver | 测试部门代表,启动快,大小写问题非常有原则 |
| Oracle | oracle.jdbc.OracleDriver | 规章制度完整,每个语法错误都有编号 |
| DB2 | com.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 字段会根据方言选择 LONGTEXT 或 CLOB,初始化器也会为查询结果注入对应的 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 和对应数据库驱动。
升级后建议至少验证:
- 启动日志中的方言是否正确。
- 单条和批量插入后主键是否回填。
- 分页、排序和关联查询生成的 SQL。
- JSON 与枚举字段的读写。
- 开启
auto-sync-db时生成的 DDL。 - 项目里的原始 SQL 是否符合目标数据库语法。
原始 SQL 不会自动经过方言翻译。你亲手写的 SQL,框架会尊重你的创作自由,也会尊重数据库拒绝它的权利。
最后
支持方言以后,Smart MyBatis 并没有变成一个庞大的数据库翻译机构。核心思路仍然没变:
- 实体和 Mapper 描述业务模型
Where描述查询意图SqlDialects负责数据库差异- MyBatis 继续负责它本来就很擅长的执行工作
这次更新的目标不是让所有数据库看起来都像 MySQL,而是让它们保持各自的脾气,同时不把脾气传递给业务代码。
详细机制请阅读数据库方言机制,完整变更和限制见3.0.2 版本说明。
欢迎升级。遇到问题可以提 Issue,请尽量附上数据库类型、驱动版本、生成 SQL 和完整异常。只发一句“不能用”也可以,只是我们双方都会获得一次猜谜体验。
