如果你想让你的 go 项目支持不同类型的数据库如:MySQL,PostgreSQL,sqlite3… 那么除了使用 orm 框架帮你屏蔽很多 sql 细节外,还有什么坑点呢?最近我正在使用 xorm 做多数据库类型的适配总结了以下问题供参考。

PS: 本人除了对 MySQL 熟悉外,其他数据库仅停留在使用和了解阶段,当前测试仅覆盖 MySQL,PostgreSQL,sqlite3 三种类型

初始化

以往我们进行数据库初始化一般就是导入需要初始化执行的 sql 文件,但由于需要支持多库,则无法使用功能,因为各个数据库结构不同,我们需要使用 xorm 的功能进行初始化操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
engine, err := xorm.NewEngine(driver, connection)  
if err != nil {
return err
}

if err = engine.Ping(); err != nil {
return err
}

err = engine.Sync(tables...)
if err != nil {
return err
}
1
2
3
4
tables = []interface{}{  
&entity.User{},
&entity.Goods{},
}

使用 Sync 就可以将我们实体类对象创建为对应的表。

注意点

  • Sync 只会做创建表的操作,或者是创建表字段的操作,对于已有的表结构中的字段无法进行修改
  • 实体类必须包含相关字段 tag 标识用于识别为字段名称类型等

字段

1
2
3
4
5
6
7
8
type User struct {
ID int `xorm:"not null pk autoincr INT(11) id"`
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
Username string `xorm:"not null default '' VARCHAR(50) UNIQUE username"`
Password string `xorm:"not null default '' VARCHAR(100) password"`
IsAdmin bool `xorm:"not null default false BOOL is_admin"`
}

对于字段,只需要根据 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 升级文件,其中就包含了变更的操作,有时会根据不同类型的数据库做出不同的操作。

gitea-migrations.png

其他提醒

  • sqlite3 需要使用 CGO,故不能使用 CGO_ENABLED=0 进行编译
  • sqlite3 至当前文章发布前,v2 版本还并没有 release,所以 gomod 引用时一定要注意并不要使用,以免引起意外

总结

  1. 初始化各个数据库表现良好,都能按需初始化成功,无需担心
  2. 字段类型和索引基本 xorm 都有良好支持,按照规范来走就没问题
  3. 注释当前只支持 MYSQL,但由于别的数据库执行会报错,故只能全部没有注释
  4. 特殊 SQL 功能如 RIGHT JOIN,部分数据库无法支持
  5. 数据库初始化之后,升级针对数据库的变更,如果只是添加字段可以使用 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