前几天看到消息Facebook孵化的ORM ent转为正式项目,出去好奇,简单体验了一下,使用上本身感受比GORM好用,因而打算把官方的文档进行整理,也算是学习一下如何使用。mysql
ent orm 须要使用entc命令进行自动代码生成,因此须要先安装entc:git
go get github.com/facebook/ent/cmd/entc
github
关于这个系列的全部代码笔记都会放到sql
github.com/peanut-cc/ent_orm_notes
shell
正常状况下应该是在本身的项目根目录执行下面的命令,我这里是由于后续会有多个例子,为了让每一个例子的内容独立,因此这里会在子目录下执行该命令数据库
entc init User
tcp
这个命令执行后生成以下目录结构:学习
└── quick_user_example └── ent ├── generate.go └── schema └── user.go
而schema目录下的user.go的内容也很是简单:ui
package schema import "github.com/facebook/ent" // User holds the schema definition for the User entity. type User struct { ent.Schema } // Fields of the User. func (User) Fields() []ent.Field { return nil } // Edges of the User. func (User) Edges() []ent.Edge { return nil }
给schema添加字段很是简单,只须要在生成ent/schema/user.go
的Fields
方法中添加便可,修改以后的代码以下:this
package schema import ( "github.com/facebook/ent" "github.com/facebook/ent/schema/field" ) // User holds the schema definition for the User entity. type User struct { ent.Schema } // Fields of the User. // 用于给 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 nil }
执行go generate ./ent
自动生成代码,执行命令后的目录结构为:
└── quick_user_example └── ent ├── client.go ├── config.go ├── context.go ├── ent.go ├── enttest │ └── enttest.go ├── generate.go ├── hook │ └── hook.go ├── migrate │ ├── migrate.go │ └── schema.go ├── mutation.go ├── predicate │ └── predicate.go ├── privacy │ └── privacy.go ├── runtime │ └── runtime.go ├── runtime.go ├── schema │ └── user.go ├── tx.go ├── user │ ├── user.go │ └── where.go ├── user_create.go ├── user_delete.go ├── user.go ├── user_query.go └── user_update.go
建立表并进行简单的添加数据,和查询数据:
package main import ( "context" "fmt" "log" _ "github.com/go-sql-driver/mysql" "github.com/peanut-pg/ent_orm_notes/quick_user_example/ent" "github.com/peanut-pg/ent_orm_notes/quick_user_example/ent/user" ) func main() { client, err := ent.Open("mysql", "root:123456@tcp(192.168.1.104:3306)/ent_orm?parseTime=True") if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // run the auto migration tool if err := client.Schema.Create(ctx); err != nil { log.Fatalf("failed creating schema resources:%v", err) } CreateUser(ctx, client) peanut, err := QueryUser(ctx, client) if err != nil { log.Fatalln(err) } log.Fatalf("query user name is:%v, aget is %v", peanut.Name, peanut.Age) } // CreateUser 建立用户 name=peanut, age=18 func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) { u, err := client.User. Create(). SetAge(18). SetName("peanut"). Save(ctx) if err != nil { return nil, fmt.Errorf("failed creating user: %v", err) } log.Println("user was created: ", u) return u, nil } // QueryUser 查询用户 where name=peanut func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) { u, err := client.User. Query(). Where(user.NameEQ("peanut")). // `Only` fails if no user found, // or more than 1 user returned. Only(ctx) if err != nil { return nil, fmt.Errorf("failed querying user: %v", err) } log.Println("user returned: ", u) return u, nil }
仍是用一样的方法建立Car和Group 的schema
entc init Car Group
分别给ent/schema
目录下的car.go
和group.go
添加对应的字段信息
car.go
文件:
package schema import ( "github.com/facebook/ent" "github.com/facebook/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 nil }
group.go
文件:
package schema import ( "regexp" "github.com/facebook/ent" "github.com/facebook/ent/schema/field" ) // Group holds the schema definition for the Group entity. type Group struct { ent.Schema } // Fields of the Group. func (Group) Fields() []ent.Field { return []ent.Field{ field.String("name"). // regexp validation for group name. Match(regexp.MustCompile("[a-zA-Z_]+$")), } } // Edges of the Group. func (Group) Edges() []ent.Edge { return nil }
在ent orm 中给表之间创建关系是经过Edges方法实现的,咱们更改ent/schema/user.go
中的Edges方法:
package schema import ( "github.com/facebook/ent" "github.com/facebook/ent/schema/edge" "github.com/facebook/ent/schema/field" ) // User holds the schema definition for the User entity. type User struct { ent.Schema } // Fields of the User. // 用于给 user 表定义字段 func (User) Fields() []ent.Field { return []ent.Field{ field.Int("age"). Positive(), field.String("name").Default("unknown"), } } // Edges of the User. // 和Cars表创建关系 func (User) Edges() []ent.Edge { return []ent.Edge{ edge.To("cars", Car.Type), } }
执行go generate ./ent
自动生成代码,而后从新生成一下表结构
而后在数据中执行show create table ent_orm.cars
查看表的详细结构语句:
CREATE TABLE `cars` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `model` varchar(255) COLLATE utf8mb4_bin NOT NULL, `registered_at` timestamp NULL DEFAULT NULL, `user_cars` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `cars_users_cars` (`user_cars`), CONSTRAINT `cars_users_cars` FOREIGN KEY (`user_cars`) REFERENCES `users` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
能够看出,经过在印记功能创建的外键关系
// CreateCars 建立 Tesla 和Ford 汽车,加该汽车属于user: peanut_pg func CreateCars(ctx context.Context, client *ent.Client) (*ent.User, error) { // creating new car with model "Tesla". tesla, err := client.Car. Create(). SetModel("Tesla"). SetRegisteredAt(time.Now()). Save(ctx) if err != nil { return nil, fmt.Errorf("failed creating car: %v", err) } // creating new car with model "Ford". ford, err := client.Car. Create(). SetModel("Ford"). SetRegisteredAt(time.Now()). Save(ctx) if err != nil { return nil, fmt.Errorf("failed creating car: %v", err) } log.Println("car was created: ", ford) // create a new user, and add it the 2 cars. peanut_pg, err := client.User. Create(). SetAge(18). SetName("peanut_pg"). // AddCars 将车属于user peanut_pg AddCars(tesla, ford). Save(ctx) if err != nil { return nil, fmt.Errorf("failed creating user: %v", err) } log.Println("user was created: ", peanut_pg) return peanut_pg, nil }
代码内容以下:
package main import ( "context" "fmt" "log" "time" _ "github.com/go-sql-driver/mysql" "github.com/peanut-pg/ent_orm_notes/quick_user_example/ent" "github.com/peanut-pg/ent_orm_notes/quick_user_example/ent/car" "github.com/peanut-pg/ent_orm_notes/quick_user_example/ent/user" ) func main() { client, err := ent.Open("mysql", "root:123456@tcp(192.168.1.104:3306)/ent_orm?parseTime=True") if err != nil { log.Fatal(err) } defer client.Close() ctx := context.Background() // run the auto migration tool if err := client.Schema.Create(ctx); err != nil { log.Fatalf("failed creating schema resources:%v", err) } peanut_pg, err := QueryUserByName(ctx, client, "peanut_pg") if err != nil { log.Fatalln(err) } QueryCars(ctx, peanut_pg) } // QueryUserByName 经过name 查询 func QueryUserByName(ctx context.Context, client *ent.Client, name string) (*ent.User, error) { u, err := client.User. Query(). Where(user.NameEQ(name)). // `Only` fails if no user found, // or more than 1 user returned. Only(ctx) if err != nil { return nil, fmt.Errorf("failed querying user: %v", err) } log.Println("user returned: ", u) return u, nil } // QueryCars 查询用户peanut_pg是否有Ford 这个车 func QueryCars(ctx context.Context, peanut_pg *ent.User) error { cars, err := peanut_pg.QueryCars().All(ctx) if err != nil { return fmt.Errorf("failed querying user cars: %v", err) } log.Println("returned cars:", cars) // what about filtering specific cars. ford, err := peanut_pg.QueryCars(). Where(car.ModelEQ("Ford")). Only(ctx) if err != nil { return fmt.Errorf("failed querying user cars: %v", err) } log.Println(ford) return nil }
在日常的查询中咱们还会常常用到一些反向查询,如咱们想要查询这个车所属的用户是谁,这个时候须要修改
ent/schema/car.go
中的Edges方法:
package schema import ( "github.com/facebook/ent" "github.com/facebook/ent/schema/edge" "github.com/facebook/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). // create an inverse-edge called "owner" of type `User` // and reference it to the "cars" edge (in User schema) // explicitly using the `Ref` method. Ref("cars"). // setting the edge to unique, ensure // that a car can have only one owner. Unique(), } }
先经过QueryCarByModel
查询一个Model=Tesla的汽车,而后经过QueryCarUser
查看这个汽车的所属者是谁
// QueryCarByModel 查询car.model=Tesla func QueryCarByModel(ctx context.Context, client *ent.Client) (*ent.Car, error) { car, err := client.Car.Query(). Where(car.ModelEQ("Tesla")). Only(ctx) if err != nil { return nil, fmt.Errorf("failed query car") } return car, nil } // QueryCarUser 查询car.model=Tesla的所属者是谁 func QueryCarUser(ctx context.Context, car *ent.Car) error { owner, err := car.QueryOwner().Only(ctx) if err != nil { return fmt.Errorf("failed querying car %q owner:%v", car.Model, err) } log.Printf("car %q owner: %q\n", car.Model, owner.Name) return nil }
在上面的关系上再添加一个用户和组的关系,分别修改ent/schema/user.go
和ent/schema/car.go
的Edges方法
// Edges of the User. // 和Cars表创建关系 func (User) Edges() []ent.Edge { return []ent.Edge{ edge.To("cars", Car.Type), // create an inverse-edge called "groups" of type `Group` // and reference it to the "users" edge (in Group schema) // explicitly using the `Ref` method. edge.From("groups", Group.Type). Ref("users"), } }
// Edges of the Car. func (Car) Edges() []ent.Edge { return []ent.Edge{ edge.From("owner", User.Type). // create an inverse-edge called "owner" of type `User` // and reference it to the "cars" edge (in User schema) // explicitly using the `Ref` method. Ref("cars"). // setting the edge to unique, ensure // that a car can have only one owner. Unique(), } }
执行go generate ./ent
自动生成代码
经过以下方法生成基础数据:
// CreateGraph 建立基础数据 func CreateGraph(ctx context.Context, client *ent.Client) error { // first, create the users. a8m, err := client.User. Create(). SetAge(30). SetName("Ariel"). Save(ctx) if err != nil { return err } neta, err := client.User. Create(). SetAge(28). SetName("Neta"). Save(ctx) if err != nil { return err } // then, create the cars, and attach them to the users in the creation. _, err = client.Car. Create(). SetModel("TeslaY"). SetRegisteredAt(time.Now()). // ignore the time in the graph. SetOwner(a8m). // attach this graph to Ariel. Save(ctx) if err != nil { return err } _, err = client.Car. Create(). SetModel("TeslaX"). SetRegisteredAt(time.Now()). // ignore the time in the graph. SetOwner(a8m). // attach this graph to Ariel. Save(ctx) if err != nil { return err } _, err = client.Car. Create(). SetModel("TeslaS"). SetRegisteredAt(time.Now()). // ignore the time in the graph. SetOwner(neta). // attach this graph to Neta. Save(ctx) if err != nil { return err } // create the groups, and add their users in the creation. _, err = client.Group. Create(). SetName("GitLab"). AddUsers(neta, a8m). Save(ctx) if err != nil { return err } _, err = client.Group. Create(). SetName("GitHub"). AddUsers(a8m). Save(ctx) if err != nil { return err } log.Println("The graph was created successfully") return nil }
三种查询例子
// QueryGithub 查询group = GitHub 的用户的全部的汽车 func QueryGithub(ctx context.Context, client *ent.Client) error { cars, err := client.Group. Query(). Where(group.Name("GitHub")). QueryUsers(). QueryCars(). All(ctx) if err != nil { return fmt.Errorf("failed getting cars:%v", err) } // cars returned: [Car(id=3, model=TeslaY, registered_at=Tue Aug 25 00:43:55 2020) Car(id=4, model=TeslaX, registered_at=Tue Aug 25 00:43:55 2020)] log.Println("cars returned:", cars) return nil }
func QueryArielCars(ctx context.Context, client *ent.Client) error { // Get "Ariel" from previous steps. a8m := client.User. Query(). Where( user.HasCars(), user.Name("Ariel"), ). OnlyX(ctx) cars, err := a8m. // Get the groups, that a8m is connected to: QueryGroups(). // (Group(Name=GitHub), Group(Name=GitLab),) QueryUsers(). // (User(Name=Ariel, Age=30), User(Name=Neta, Age=28),) QueryCars(). // Where( // car.Not( // Get Neta and Ariel cars, but filter out car.ModelEQ("TeslaX"), // those who named "Mazda" ), ). All(ctx) if err != nil { return fmt.Errorf("failed getting cars: %v", err) } log.Println("cars returned:", cars) // Output: (Car(Model=Tesla, RegisteredAt=<Time>), Car(Model=Ford, RegisteredAt=<Time>),) return nil }
// QueryGroupWithUsers 查询全部由用户的组 func QueryGroupWithUsers(ctx context.Context, client *ent.Client) error { groups, err := client.Group. Query(). Where(group.HasUsers()). All(ctx) if err != nil { return fmt.Errorf("failed getting groups: %v", err) } log.Println("groups returned:", groups) // Output: (Group(Name=GitHub), Group(Name=GitLab),) return nil }