【GoCN酷Go推荐】简单强大ORM框架entgo

共 12524字,需浏览 26分钟

 ·

2021-04-19 15:41

简介

ent 是一个简单而又强大的Go实体框架,便于构建和使用大型数据模型时能够遵循以下原则:

  • 简单地使用数据库结构作为图结构。
  • 使用Go代码定义结构。
  • 基于代码生成的静态类型。
  • 容易地进行数据库查询和图遍历。
  • 容易地使用Go模板扩展和自定义。

应用场景 

entgo非常适合处理各种复杂的关系,定义好实体和实体之间的关系,就可以快速得到各种想要的数据


核心概念 

Schema:描述一个实体的定义以及他与其他实体的关系

  Edges:实体与实体之间的关系称为edge

如何使用

安装

go get entgo.io/ent/cmd/ent

创建Schema

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"

    "<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)
    }
}

有了实体就可以进行增删改查了

新增:

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:

建立一个新的实体CarUser有多个CarCar属于某个User

er-user-cars

文件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"

    "<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
}

查询汽车们属于谁:

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

更多文档查阅官网:https://entgo.io/zh/docs/getting-started

总结
entgo与传统orm不太一样,通过自动生成的代码可以很方便的进行数据库查询和图遍历,使用起来简单清晰,可以很好的协助我们完成数据建模工作。

还想了解更多吗?

    更多请查看:https://github.com/google/wire

    欢迎加入我们GOLANG中国社区:https://gocn.vip/


《酷Go推荐》招募:


各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到新的库,并且知道怎么用。


大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!


点击 阅读原文 即刻报名


— 往期回顾 —

【GoCN酷Go推荐】Go程序配置利器-viper

【GoCN酷Go推荐】Validator 网络请求数据验证包

【GoCN酷Go推荐】ip2location 解析 IP 地址库



浏览 50
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报