安装

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

奇技淫巧

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

继承

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

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

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() 来指定要忽略的字段,查询和更新都可以使用。

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

Migration

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

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 设置更新全部字段:

_, 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 及以后的版本,要么修改默认配置。

I hope this is helpful, Happy hacking…