一直以来,基本上 Go 的ORM都是用 GORM 。直到前段时间朋友推荐了 entgo,
尝试之后发现 entgo 是更好的选择。entgo 是 Facebook 开源的一个基于 go generate
生成的 ORM,但是并不算复杂,相比 GORM 的好处在于,GORM中,大量存在
interface{}
传参,因此很难通过编译器检查一些本不应该有的错误,而 entgo
则通过 go generate
的方式生成带类型的代码。我个人认为这是 entgo 最大的优势。
首先需要初始化一个项目,比如 go mod init github.com/jiajunhuang/test
,
然后安装 entgo:
$ go get entgo.io/ent/cmd/ent
$
接下来就可以开始定义 schema 了,首先生成一个:
$ go run entgo.io/ent/cmd/ent init User
$
这是 entgo 和 GORM 比较不一样的地方,GORM将类型放在代码中,并且会在后续直接使用, 而 entgo 由于需要生成一些数据操作,所以定义的 schema 更像是一个元信息。
接上文,生成之后,编辑 schema:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// User 定义User类型
type User struct {
ent.Schema
}
// Fields 方法写明有什么字段
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").Positive(),
field.String("name").Default("unknown"),
}
}
// Edges 写明与其它schema的关系,相当于ER图中的关系
func (User) Edges() []ent.Edge {
return nil
}
// Indexes 写明索引
func (User) Indexes() []ent.Index {
return []ent.Index{
// 非唯一的联合索引
index.Fields("age", "name"),
// 非唯一的普通索引
index.Fields("age"),
// 唯一索引
index.Fields("name").Unique(),
}
}
然后执行命令生成代码:
$ go generate ./ent
$
这个时候就可以开始导入包并且使用了。为了方便,直接看官网例子:
注意,里面的 where 例子中,小写的user,是要从包里导入:
import "github.com/jiajunhuang/test/env/user"
还有一点就是,如果不希望数据被修改,还可以在 Fields()
中设置 Mutation:
func (Pair) Fields() []ent.Field {
return []ent.Field{
field.String("pair_symbol").MaxLen(64).NotEmpty().Immutable(),
}
}
可以通过 Mixin 来复用一些定义:
package schema
import (
"time"
"entgo.io/ent"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/mixin"
)
// -------------------------------------------------
// Mixin definition
// TimeMixin implements the ent.Mixin for sharing
// time fields with package schemas.
type TimeMixin struct {
// We embed the `mixin.Schema` to avoid
// implementing the rest of the methods.
mixin.Schema
}
func (TimeMixin) Fields() []ent.Field {
return []ent.Field{
field.Time("created_at").
Immutable().
Default(time.Now),
field.Time("updated_at").
Default(time.Now).
UpdateDefault(time.Now),
field.Bool("deleted").Default(false),
}
}
// 要记得在User里增加这个方法
func (User) Mixin() []ent.Mixin {
return []ent.Mixin{
TimeMixin{},
}
}
数据库Migration是一个很重要的事情,entgo自带了,直接在代码里:
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
就会自动做migration,但是默认情况下是只增不减的,如果想要能删除不存在的 索引和列,那么可以加:
package main
import (
"context"
"log"
"<project>/ent"
"<project>/ent/migrate"
)
func main() {
client, err := ent.Open("mysql", "root:pass@tcp(localhost:3306)/test")
if err != nil {
log.Fatalf("failed connecting to mysql: %v", err)
}
defer client.Close()
ctx := context.Background()
// Run migration.
err = client.Schema.Create(
ctx,
migrate.WithDropIndex(true),
migrate.WithDropColumn(true),
)
if err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
当然,也可以不用它而用 go-migrate 等库。
这是 entgo 的基本用法,他还有很多高级用法这里没有介绍,我觉得这篇文章还是 起一个引路人的作用,具体的还是要去官网慢慢看文档学习才有用。
Ref: