使用 xorm 实现多数据库支持坑点总结
如果你想让你的 go 项目支持不同类型的数据库如:MySQL,PostgreSQL,sqlite3… 那么除了使用 orm 框架帮你屏蔽很多 sql 细节外,还有什么坑点呢?最近我正在使用 xorm 做多数据库类型的适配总结了以下问题供参考。
PS: 本人除了对 MySQL 熟悉外,其他数据库仅停留在使用和了解阶段,当前测试仅覆盖 MySQL,PostgreSQL,sqlite3 三种类型
初始化
以往我们进行数据库初始化一般就是导入需要初始化执行的 sql 文件,但由于需要支持多库,则无法使用功能,因为各个数据库结构不同,我们需要使用 xorm 的功能进行初始化操作。
1 | engine, err := xorm.NewEngine(driver, connection) |
1 | tables = []interface{}{ |
使用 Sync
就可以将我们实体类对象创建为对应的表。
注意点
- Sync 只会做创建表的操作,或者是创建表字段的操作,对于已有的表结构中的字段无法进行修改
- 实体类必须包含相关字段 tag 标识用于识别为字段名称类型等
字段
1 | type User struct { |
对于字段,只需要根据 xorm 文档中给出的规范,写好对应的 tag 就可以 https://xorm.io/zh/docs/chapter-02/4.columns/
注意点
- 不同数据库中字段的类型不同,在 tag 中一定要书写 xorm 的类型,而非数据库的类型,比如上述所示中写的是
BOOL
在 MYSQL 中识别为TINYINT/INT
,在 sqlite3 中为INTEGER
, 在 postgres 中为BIGSERIAL
- 特别注意字符串的是否非空,由于 go 中 string 默认为 “”
索引
索引比较简单根据需求写在实体类对应字段中即可:
- 普通索引写 index,普通联合索引写 index(索引名称)
- 唯一索引写 unique,联合唯一索引写 unique(索引名称)
让 xorm 帮助我们去创建索引即可
注释
这个是最头疼的问题,当前 xorm 的 comment tag 只支持 MYSQL,并且只要写了,在其他数据库 SYNC 时候会报错,故当前暂时无法支持其他类型的库。
SQL
绝大部分的 SQL 操作由于使用了 xorm,或使用了 builder,大部分都没有问题,目前遇到的问题仅为:
1 | RIGHT and FULL OUTER JOINs are not currently supported |
在使用 RIGHT 和 FULL JOIN 操作的时候 sqlite3 是不支持的。
当然由于各个数据库特性不同,一些特殊的函数操作可能有的也不支持,需要具体测试。若要做多类型支持也尽量可以考虑不去使用。
升级
首先 xorm 的 sync 方法不能帮你修改数据库的字段,因为这是一个极其危险的操作,对于数据库升级来说往往意味着原始数据的迁移或者变动。
所以针对不同数据库的升级,往往只能采用写sql的方式进行适配性解决。(但通常我们更多的时候是添加字段,那么 xorm 的 sync 方法还是很好用的。)
如 gitea 也是支持了多种类型的数据库,它在每次升级都会执行一个 go 升级文件,其中就包含了变更的操作,有时会根据不同类型的数据库做出不同的操作。
其他提醒
- sqlite3 需要使用 CGO,故不能使用
CGO_ENABLED=0
进行编译 - sqlite3 至当前文章发布前,v2 版本还并没有 release,所以 gomod 引用时一定要注意并不要使用,以免引起意外
总结
- 初始化各个数据库表现良好,都能按需初始化成功,无需担心
- 字段类型和索引基本 xorm 都有良好支持,按照规范来走就没问题
- 注释当前只支持 MYSQL,但由于别的数据库执行会报错,故只能全部没有注释
- 特殊 SQL 功能如 RIGHT JOIN,部分数据库无法支持
- 数据库初始化之后,升级针对数据库的变更,如果只是添加字段可以使用 xorm 统一处理,字段或者索引修改往往只能通过执行 sql 实现
后续更新
这个部分记录后续使用过程中出现的各种兼容性问题
PostgreSQL 中 user 是关键字
出现错误 pq: syntax error at or near “.”
在正常的表连接语句中会使用 user.id = other.user_id
这样的写法,在 MySQL 中这样没有问题,但由于 PostgreSQL 中 user
是一个关键字,所以,会出现报错。
解决方法就是给 user 添加引号:
1 | `user`.id = other.user_id |