快速开始

简介

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
}

添加第一条逆向边(反向引用)

结构

引言

代码生成

迁移


sayornottt
1 声望0 粉丝

姑且是测试开发工程师.