简介
ent 是一个简单而又强大的Go实体框架,便于构建和使用大型数据模型时能够遵循以下原则:
简单地使用数据库结构作为图结构。
使用Go代码定义结构。
基于代码生成的静态类型。
容易地进行数据库查询和图遍历。
容易地使用Go模板扩展和自定义。
应用场景
entgo
非常适合处理各种复杂的关系,定义好实体和实体之间的关系,就可以快速得到各种想要的数据
核心概念
Schema:描述一个实体的定义以及他与其他实体的关系
Edges:实体与实体之间的关系称为edge
如何使用
安装
go get entgo.io/ent/cmd/ent
ent init --target internal/ent/schema User
运行之后会在internal/ent/schema目录下生成一个user.go文件,并进行编辑:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
// 年龄是自然数
field.Int("age").Positive(),
field.String("name").Default("unknown"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
}
}
ent generate ./internal/ent/schema
运行之后会自动创建以下文件:
ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── migrate
│ ├── migrate.go
│ └── schema.go
├── predicate
│ └── predicate.go
├── schema
│ └── user.go
├── tx.go
├── user
│ ├── user.go
│ └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go
package main
import (
"context"
"log"
"/ent"
_ "github.com/mattn/go-sqlite3"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()
// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
}
有了实体就可以进行增删改查了
新增:
func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
u, err := client.User.
Create().
SetAge(30).
SetName("a8m").
Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed creating user: %w", err)
}
log.Println("user was created: ", u)
return u, nil
}
修改:
func UpdateUser(ctx context.Context, a8m *ent.User) (*ent.User, error) {
u, err := a8m.
Update().
SetAge(28).
SetName("neta").
Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed update user: %w", err)
}
log.Println("user was updated: ", u)
return u, nil
}
删除:
func DeleteUser(ctx context.Context, client *ent.Client, a8m *ent.User) error {
err := client.User.
DeleteOne(u).
Save(ctx)
if err != nil {
return fmt.Errorf("failed delete user: %w", err)
}
log.Println("user was deleted: ", u)
return nil
}
查询:
func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
u, err := client.User.
Query().
Where(user.NameEQ("a8m")).
// `Only` 在 找不到用户 或 找到多于一个用户 时报错,
Only(ctx)
if err != nil {
return nil, fmt.Errorf("failed querying user: %w", err)
}
log.Println("user returned: ", u)
return u, nil
}
Edges:
建立一个新的实体Car
,User
有多个Car
,Car
属于某个User
文件car.go:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// Car holds the schema definition for the Car entity.
type Car struct {
ent.Schema
}
// Fields of the Car.
func (Car) Fields() []ent.Field {
return []ent.Field{
field.String("model"),
field.Time("registered_at"),
}
}
// Edges of the Car.
func (Car) Edges() []ent.Edge {
return []ent.Edge{
edge.
From("owner", User.Type).
Ref("cars").
Unique(),
}
}
修改user.go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/mixin"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").Positive(),
field.String("name").Default("unknown"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("cars", Car.Type),
}
}
建立好关系之后,创建2辆汽车并将他们添加到某个用户:
func CreateCars(ctx context.Context, client *ent.Client) (*ent.User, error) {
// 创建一辆车型为 "Tesla" 的汽车.
tesla, err := client.Car.
Create().
SetModel("Tesla").
SetRegisteredAt(time.Now()).
Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed creating car: %w", err)
}
log.Println("car was created: ", tesla)
// 创建一辆车型为 "Ford" 的汽车.
ford, err := client.Car.
Create().
SetModel("Ford").
SetRegisteredAt(time.Now()).
Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed creating car: %w", err)
}
log.Println("car was created: ", ford)
// 新建一个用户,将两辆车添加到他的名下
a8m, err := client.User.
Create().
SetAge(30).
SetName("a8m").
AddCars(tesla, ford).
Save(ctx)
if err != nil {
return nil, fmt.Errorf("failed creating user: %w", err)
}
log.Println("user was created: ", a8m)
return a8m, nil
}
查询某人拥有的cars:
import (
"log"
"/ent"
"/ent/car"
)
func QueryCars(ctx context.Context, a8m *ent.User) error {
cars, err := a8m.QueryCars().All(ctx)
if err != nil {
return fmt.Errorf("failed querying user cars: %w", err)
}
log.Println("returned cars:", cars)
// 筛选特定汽车的情况
ford, err := a8m.QueryCars().
Where(car.ModelEQ("Ford")).
Only(ctx)
if err != nil {
return fmt.Errorf("failed querying user cars: %w", err)
}
log.Println(ford)
return nil
}
查询汽车们属于谁:
import (
"fmt"
"log"
"/ent"
)
func QueryCarUsers(ctx context.Context, a8m *ent.User) error {
cars, err := a8m.QueryCars().All(ctx)
if err != nil {
return fmt.Errorf("failed querying user cars: %w", err)
}
// Query the inverse edge.
for _, ca := range cars {
owner, err := ca.QueryOwner().Only(ctx)
if err != nil {
return fmt.Errorf("failed querying car %q owner: %w", ca.Model, err)
}
log.Printf("car %q owner: %q\n", ca.Model, owner.Name)
}
return nil
}
更多示例参考:https://github.com/ent/ent/tree/master/examples/start
更多文档查阅官网:https://entgo.io/zh/docs/getting-started
总结
entgo
与传统orm不太一样,通过自动生成的代码可以很方便的进行数据库查询和图遍历,使用起来简单清晰,可以很好的协助我们完成数据建模工作。
更多请查看:https://github.com/google/wire
欢迎加入我们GOLANG中国社区:https://gocn.vip/
《酷Go推荐》招募:
各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到新的库,并且知道怎么用。
大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!
点击 阅读原文 即刻报名
— 往期回顾 —