Go 用 ent 构建类型安全 ORM:实体定义、关联与代码生成

test12026-10-2915 次阅读

为什么选 ent

GORM 用反射在运行时拼查询,字段名写错要等跑起来才发现;sqlc 把 SQL 编译成 Go 但偏向手写 SQL。ent 走另一条路:用 Go 代码描述 schema,再用代码生成器产出带类型的方法和查询构造器,字段名、关联在编译期就确定。

一、定义实体

用 ent 的 schema 描述一个 User 与 Post 的一对多关系。

// ent/schema/user.go
package schema

import (
	"entgo.io/ent"
	"entgo.io/ent/schema/field"
	"entgo.io/ent/schema/edge"
)

type User struct {
	ent.Schema
}

func (User) Fields() []ent.Field {
	return []ent.Field{
		field.String("name").NotEmpty(),
		field.Int("age").Positive(),
	}
}

func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("posts", Post.Type),
	}
}

二、生成代码并查询

跑 ent generate 后,会得到一个带类型的客户端。查询时字段是编译期已知的,拼错字段名直接红。

client, _ := ent.Open("sqlite", "file:ent.db?cache=shared&_fk=1")
defer client.Close()

// 创建并连带创建帖子
u, _ := client.User.
	Create().
	SetName("Ada").
	SetAge(30).
	AddPosts(
		client.Post.Create().SetTitle("hello").SaveX(ctx),
	).
	SaveX(ctx)

// 查某用户的所有帖子,posts 是强类型切片
posts, _ := u.QueryPosts().All(ctx)
for _, p := range posts {
	_ = p.Title // 编译期已知字段
}

三、用 predicate 做类型安全过滤

ent 为每个字段生成 predicate 函数,条件组合在编译期可检查,不会出现字符串拼错列名。

import "yourproject/ent/user"

users, _ := client.User.
	Query().
	Where(
		user.AgeGT(18),
		user.NameContains("A"),
	).
	Order(user.ByAge()).
	All(ctx)

四、迁移

ent 自带 migrate 包,启动时按 schema 自动建表,开发期很省心。

client.Schema.Create(ctx)

小结

ent 的代价是要接受代码生成和一套自己的查询 DSL,换来的是字段与关联全程类型安全:改了 schema,调用方没跟着改就会编译失败。对字段多、关联复杂的业务,这份编译期担保很值。

T

test1

文章作者

为什么选 ent GORM 用反射在运行时拼查询,字段名写错要等跑起来才发现;sqlc 把 SQL 编译成 Go 但偏向...

分类
技术
发布时间
2026-10-29
字数
约 1355 字
阅读
15 次