簡單強大 ORM 框架 entgo

簡介

ent 是一個簡單而又強大的 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 不太一樣,通過自動生成的代碼可以很方便的進行數據庫查詢和圖遍歷,使用起來簡單清晰,可以很好的協助我們完成數據建模工作。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/4PC8YpiLbPBoiexOCTjtVA