XORM 项目实践总结

XORM 项目实践总结

工作中的项目使用的是 Go 来进行开发,在 ORM 的选择方面我们最终选择的是 XORM,实践中遇到很多问题,但是官方文档又较少,所以这里分享一些奇技淫巧。

安装

1
2
3
4
$ go get xorm.io/xorm

# 安装对应的数据库驱动
$ go get github.com/go-sql-driver/mysql

奇技淫巧

官方文档里有的我这里就不再赘述了,下面将介绍一些有用但官方文档里没有的内容。

继承

当我们的业务中多个模型中的部分字段是相同的,一般做法是在每个模型中重复定义,这样做不仅费时费力,而且容易出错。那么 XORM 可以在 Tag 中指定 extends 来实现对 Struct 的继承,举个例子:

如业务中我们需要去存储 Google、Baidu 的统计数据,很多字段是可以公用的,那么我们可以定义一个单独的 Struct 来存放这些公共的字段。然后在需要的 Struct 中进行继承即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Report struct {
Date string `json:"date" xorm:"DATE NOT NULL COMMENT('日期')"`
Impression float64 `json:"impression" xorm:"INT(11) NOT NULL DEFAULT 0 COMMENT('展现')"`
Click float64 `json:"click" xorm:"INT(11) NOT NULL DEFAULT 0 COMMENT('点击')"`
Cost float64 `json:"cost" xorm:"FLOAT NOT NULL DEFAULT 0 COMMENT('消费')"`
Ctr float64 `json:"ctr" xorm:"FLOAT NOT NULL DEFAULT 0 COMMENT('点击率')"`
Cpm float64 `json:"cpm" xorm:"FLOAT NOT NULL DEFAULT 0 COMMENT('均价')"`
Pv float64 `json:"pv" xorm:"INT(11) NOT NULL DEFAULT 0 COMMENT('页面浏览量')"`
Uv float64 `json:"uv" xorm:"INT(11) NOT NULL DEFAULT 0 COMMENT('访客数量')"`
}

type ReportBaidu struct {
Id int64 `json:"id" xorm:"BIGINT(20) PK AUTOINCR COMMENT('ID')"`
Report `xorm:"extends"`
Ar float64 `json:"ar" xorm:"FLOAT NOT NULL DEFAULT 0 COMMENT('抵达率')"`
CreatedAt Time `json:"created_at" xorm:"DATETIME NOT NULL CREATED COMMENT('创建时间')"`
}

type ReportGoogle struct {
Id int64 `json:"id" xorm:"BIGINT(20) PK AUTOINCR COMMENT('ID')"`
Report `xorm:"extends"`
CreatedAt Time `json:"created_at" xorm:"DATETIME NOT NULL CREATED COMMENT('创建时间')"`
}

忽略字段

使用 Omit() 来指定要忽略的字段,查询和更新都可以使用。

1
_, err := Engine.Where(builder.Eq{"id": report.Id}).Omit("updated_at").Update(report)

Migration

用过 Laravel 的都知道 Laravel 使用 migration 来管理项目的数据表,那么在 XORM 中也有实现,只不过并非是开箱即用的。执行 migrate 的逻辑需要自己实现。如下代码所示,我们采用 XORM 提供的 Sync2 方法进行 migrate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package models

import (
"xorm.io/xorm"
"xorm.io/xorm/migrate"
)

var tables = []interface{}{
&Job{},
//&RawData{},
ReportTotal{},
&ReportBaidu{},
&ReportUmeng{},
&ReportApplet{},
&ReportGoogle{},
}

var migrations = []*migrate.Migration{
{
ID: "2020071502",
Migrate: func(engine *xorm.Engine) error {
return engine.Charset("utf8mb4").Sync2(tables...)
},
Rollback: func(engine *xorm.Engine) error {
return engine.DropTables(tables...)
},
},
}

func Migrate() error {
m := migrate.New(Engine, &migrate.Options{
TableName: "migrations",
IDColumnName: "id",
}, migrations)

return m.Migrate()
}

只需要在业务逻辑中调用 Migrate 函数即可实现迁移。

Seed

这部分内容可以参考我之前的文章Go 实现 Database Seeder

遇到的坑

Update

在实际的项目中,我发现当我们进行 Update 某条数据时,如果某个字段为零值则这个字段会被忽略,比如当前 Uv 字段值为 10,Update 时的 Uv 值设为 0 时,更新后的结果是 Uv 依然为 10。

为了避免这种问题我们需要指定更新字段,可以使用 Cols 来指定部分字段,也可以直接使用 AllCols 设置更新全部字段:

1
2
_, err := Engine.Where(builder.Eq{"id": report.Id}).Cols("uv","pv").Update(report)
_, err := Engine.Where(builder.Eq{"id": report.Id}).AllCols().Update(report)

Migrations

Migrations 表的 ID 字段使用的是 VARCHAR(255) ,如果在 MySQL 5.7 以前的版本,执行迁移时会报错,原因是主键的长度超出了最大限制。遇到这种情况,要么使用 5.7 及以后的版本,要么修改默认配置。

# orm

评论