【GoCN酷Go推薦】簡(jiǎn)單強(qiáng)大ORM框架entgo
簡(jiǎn)介
ent 是一個(gè)簡(jiǎn)單而又強(qiáng)大的Go實(shí)體框架,便于構(gòu)建和使用大型數(shù)據(jù)模型時(shí)能夠遵循以下原則:
簡(jiǎn)單地使用數(shù)據(jù)庫(kù)結(jié)構(gòu)作為圖結(jié)構(gòu)。 使用Go代碼定義結(jié)構(gòu)。 基于代碼生成的靜態(tài)類型。 容易地進(jìn)行數(shù)據(jù)庫(kù)查詢和圖遍歷。 容易地使用Go模板擴(kuò)展和自定義。
應(yīng)用場(chǎng)景
entgo非常適合處理各種復(fù)雜的關(guān)系,定義好實(shí)體和實(shí)體之間的關(guān)系,就可以快速得到各種想要的數(shù)據(jù)
核心概念
Schema:描述一個(gè)實(shí)體的定義以及他與其他實(shí)體的關(guān)系
Edges:實(shí)體與實(shí)體之間的關(guān)系稱為edge
如何使用
安裝
go get entgo.io/ent/cmd/ent
創(chuàng)建Schema
ent init --target internal/ent/schema User
運(yùn)行之后會(huì)在internal/ent/schema目錄下生成一個(gè)user.go文件,并進(jìn)行編輯:
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{
// 年齡是自然數(shù)
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
運(yùn)行之后會(huì)自動(dòng)創(chuàng)建以下文件:
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
創(chuàng)建實(shí)體
package main
import (
"context"
"log"
"<project>/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)
}
}
有了實(shí)體就可以進(jìn)行增刪改查了
新增:
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` 在 找不到用戶 或 找到多于一個(gè)用戶 時(shí)報(bào)錯(cuò),
Only(ctx)
if err != nil {
return nil, fmt.Errorf("failed querying user: %w", err)
}
log.Println("user returned: ", u)
return u, nil
}
Edges:
建立一個(gè)新的實(shí)體Car,User有多個(gè)Car,Car屬于某個(gè)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),
}
}
建立好關(guān)系之后,創(chuàng)建2輛汽車并將他們添加到某個(gè)用戶:
func CreateCars(ctx context.Context, client *ent.Client) (*ent.User, error) {
// 創(chuàng)建一輛車型為 "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)
// 創(chuàng)建一輛車型為 "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)
// 新建一個(gè)用戶,將兩輛車添加到他的名下
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"
"<project>/ent"
"<project>/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
}
查詢汽車們屬于誰(shuí):
import (
"fmt"
"log"
"<project>/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
更多文檔查閱官網(wǎng):https://entgo.io/zh/docs/getting-started
entgo與傳統(tǒng)orm不太一樣,通過(guò)自動(dòng)生成的代碼可以很方便的進(jìn)行數(shù)據(jù)庫(kù)查詢和圖遍歷,使用起來(lái)簡(jiǎn)單清晰,可以很好的協(xié)助我們完成數(shù)據(jù)建模工作。還想了解更多嗎?
更多請(qǐng)查看:https://github.com/google/wire
歡迎加入我們GOLANG中國(guó)社區(qū):https://gocn.vip/
《酷Go推薦》招募:
各位Gopher同學(xué),最近我們社區(qū)打算推出一個(gè)類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個(gè)庫(kù)或者好的項(xiàng)目,然后寫一點(diǎn)這個(gè)庫(kù)使用方法或者優(yōu)點(diǎn)之類的,這樣可以真正的幫助到大家能夠?qū)W習(xí)到新的庫(kù),并且知道怎么用。
大概規(guī)則和每日新聞?lì)愃?,如果?bào)名人多的話每個(gè)人一個(gè)月輪到一次,歡迎大家報(bào)名!
點(diǎn)擊 閱讀原文 即刻報(bào)名
— 往期回顧 —
【GoCN酷Go推薦】Go程序配置利器-viper庫(kù)

【GoCN酷Go推薦】Validator 網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)驗(yàn)證包

【GoCN酷Go推薦】ip2location 解析 IP 地址庫(kù)

