Stories

Detail Return Return

Ent.中文文檔(持續更新~) - Stories Detail

快速開始

簡介

ent 是一個簡單而強大的Go語言實體框架,它使得構建和維護大型數據模型的應用程序變得容易,同時ent遵循以下原則:

  • 輕鬆地將數據庫模式建模為圖形結構
  • 將模式定義為可編程的Go語言代碼
  • 基於代碼生成的靜態類型
  • 易於編寫的數據庫查詢和圖遍歷
  • 簡單地擴展和使用Go模版進行自定義

設置Go環境

如果你的項目目錄在GOPATH之外,或者你不熟悉GOPATH,可通過如下的命令進行Go module設置

go mod init entdemo

創建第一個模式(Schema)

在項目的根目錄運行以下命令:

go run -mod=mod entgo.io/ent/cmd/ent new <Schema-name>

該命令會在指定目錄:<project>/ent/schema/下生成指定<Schema-name>的模式(示例中Schema為User):



package schema

import "entgo.io/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中添加2個字段:


package schema

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

// Fields of the User.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.Int("age").
            Positive(),
        field.String("nickname").
            Default("unknown"),
    }
}

在項目的根目錄運行go generate命令生成文件:


    go generate ./ent

生成文件如下:


    ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... truncated
├── schema
│   └── user.go
├── tx.go
├── user
│   ├── user.go
│   └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go

創建第一個實體(Entity)

首先,創建一個新的ent.Client,示例中將使用MySQL


package main

import (
    "context"
    "log"

    "entdemo/ent"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    client, err := ent.Open("mysql", "<user>:<pass>@tcp(<host>:<port>)/<database>?parseTime=True")
    if err != nil {
        log.Fatalf("failed opening connection to mysql: %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)
    }
}

然後我們,準備創建user,示例中通過調用CreateerUser函數進行創建:


func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
    u, err := client.User.Create().
                        SetAge(18).
                        SetNickname("tester").
                        Save(ctx)
    if err != nil {
        return nil, fmt.Errorf("failed creating user: %w", err)
    }
    log.Println("user was created: ", u)
    return u, nil
}

查詢實體

ent為每一個實體模式生成了一個包含斷言、默認值、校驗器以及存儲元素(列名、主鍵等)的額外信息的包


package main
import (
    "log"
    "entdemo/ent"
    "entdemo/ent/user"
)

func QueryUser(ctx context.Context, client *ent.CLient) (*ent.User, error) {
    u, err := client.User.Query().
                          Where(user.Nickname).
                          // `Only` fails if no user found,
                          // or more than 1 user returned.
                          Only(ctx)
    if err != nil {
        return nil, fmt.Errorf("failed quering user: %w", err)
    }
    log.Println("user returned: ", u)
    return u, nil
}

添加第一個條邊(關係)

在教程的這個部分,我們在該模式中想要聲明一條到另一個實體的邊
首先創建2個額外的實體:Car和Group
同樣地,通過ent cli進行模式的初始化:


go run -mod=mod entgo.io/ent/cmd/ent new Car Group

然後手動增加一些字段:


//Fieids of the Car.
func (Car) Fields() []ent.Field {
    return []ent.Field{
        field.String("model"),
        field.Time("registered_at"),
    }
}

// Fields of the Group.
func (Group) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").
        Match(regexp.MustCompile("[a-zA-Z_]+$")),
    }
}

讓我們來定義第一個關係,從User到Car的邊意味着user可擁有一或多個car,但car只能屬於一個user(一對多的關係)

re_user_cars.png
添加“cars”邊到User模式中,然後運行go generate ./ent命令:


// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("cars", Car.Type),
    }
}

在示例中繼續創建2個car然後添加到user中:


import (
    "entdemo/ent"
    "entdemo/ent/car"
    "entdemo/ent/user"
)

func CreateCars(ctx context.Context, client *ent.Client) (*ent.User, error) {
    // Create a 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: %w", err)
    }
    log.Println("car was created: ", tesla)

    // Create a 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: %w", err)
    }
    
    // Create a new user, and add it the 2 cars.
    tester, err := client.User.Create().
                                SetAge(18).
                                SetNickname("tester").
                                AddCars(tesla, ford).
                                Save(ctx)
    if err != nil {
        return nil, fmt.Errorf("failed creating user: %w", err)
    }
log.Println("user was created: ", tester)
return tester, nil
}

查詢cars邊(關係):


import (
    "log"
    "entdemo/ent"
    "entdemo/ent/car"
)

func QueryCars(ctx context.Context, tester *ent.Client) error {
    cars, err := tester.QueryCars().All(ctx)
    if err != nil {
        return fmt.Errorf("failed querying user cars: %w", err)
    }
    log.Println("returned cars: ", cars)

    // What about filtering specific cars.
    ford, err := tester.QueryCars().
                        Where(car.Model("Ford")).
                        Only(ctx)
    if err != nil {
        return fmt.Errorf("failed querying user cars: %w", err)
    }
    log.Println(ford)
    return nil
}

添加第一條逆向邊(反向引用)

結構

引言

代碼生成

遷移

Add a new Comments

Some HTML is okay.