Go Ent 数据库 GROM 框架

发布时间: 更新时间: 总字数:1473 阅读时间:3m 作者: IP上海 分享 网址

Ent 是 Facebook 开源的,一个简单而又功能强大的 Go 语言实体框架,ent 易于构建和维护应用程序与大数据模型

介绍

  • 图就是代码:将任何数据库表建模为 Go 对象
  • 轻松地遍历任何图形:可以轻松地运行查询、聚合和遍历任何图形结构
  • 静态类型和显式 API:使用代码生成静态类型和显式 API,查询数据更加便捷
  • 多存储驱动程序:支持 MySQL, PostgreSQL, SQLite 和 Gremlin
  • 可扩展:简单地扩展和使用 Go 模板自定义

安装

go install entgo.io/ent/cmd/ent@latest

help

$ ent --help
Usage:
  ent [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  describe	print a description of the graph schema
  generate	generate go code for the schema directory
  help		Help about any command
  new		 initialize a new environment with zero or more schemas

Flags:
  -h, --help   help for ent

Use "ent [command] --help" for more information about a command.
  • new 命令
$ ent help new
initialize a new environment with zero or more schemas

Usage:
  ent new [flags] [schemas]

Examples:
  ent new Example
  ent new --target entv1/schema User Group
  ent new --template ./path/to/file.tmpl User

Flags:
  -h, --help			  help for new
	  --target string	 target directory for schemas (default "ent/schema")
	  --template string   template to use for new schemas

使用

# 初始化项目
mkdir entdemo && cd entdemo
go mod init entdemo

创建实体 User

ent new --target ent/schema User
  • entdemo/ent/schema/user.go
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  // 默认值
	// 新增字段
	return []ent.Field{
		field.Int("age").
			Positive(),
		field.String("name").
			Default("unknown"),
	}
}

// Edges of the User.
func (User) Edges() []ent.Edge {
	return nil
}

生成数据库实体

go generate ./ent

生成的目录结构:

$ tree .
.
├── ent
│   ├── client.go
│   ├── ent.go
│   ├── enttest
│   │   └── enttest.go
│   ├── generate.go
│   ├── hook
│   │   └── hook.go
│   ├── migrate
│   │   ├── migrate.go
│   │   └── schema.go
│   ├── mutation.go
│   ├── predicate
│   │   └── predicate.go
│   ├── runtime
│   │   └── runtime.go
│   ├── runtime.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
├── go.mod
└── go.sum

9 directories, 22 files

初始化数据库连接

  • entdemo/main.go
package main

import (
	"context"
	"log"

	_ "github.com/go-sql-driver/mysql" // 导入MySQL驱动

	"entdemo/ent"
)

func main() {
	// client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
	// 使用 ent.Open 建立与 MYSQL 数据库的连接
	client, err := ent.Open("mysql", "<username>:<password>@tcp(localhost:3306)/<database>?parseTime=True")
	if err != nil {
		log.Fatalf("failed opening connection to mysql: %v", err)
	}
	defer client.Close()

	// Run migration.
	ctx := context.Background()
	if err := client.Schema.Create(ctx); err != nil {
		log.Fatalf("failed creating schema resources: %v", err)
	}
	// err = client.Schema.Create(
	// 	ctx,
	// 	migrate.WithDropIndex(true),
	// 	migrate.WithDropColumn(true),
	// )
	// if err != nil {
	// 	log.Fatalf("failed creating schema resources: %v", err)
	// }

	// 创建用户实体
	u, err := CreateUser(ctx, client)
		if err != nil {
			log.Fatalf("failed creating user: %v", err)
	}
	log.Printf("created user: %#v\n", u)


	// 查询用户实体
	u, err := QueryUser(ctx, client)
	if err != nil {
		log.Fatalf("failed querying user: %v", err)
	}
	log.Printf("queried user: %#v\n", u)
}

CRUD API

Create One

a8m, err = a8m.Update().	// User update builder.
	RemoveGroup(g2).		// Remove a specific edge.
	ClearCard().			// Clear a unique edge.
	SetAge(30).			 // Set a field value.
	AddRank(10).			// Increment a field value.
	AppendInts([]int{1}).   // Append values to a JSON array.
	Save(ctx)			   // Save and return.

func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
	u, err := client.User.
		Create().
		SetAge(30).
		SetName("a8m").
		Save(ctx) // 调用 Save 将实体保存到数据库中
	if err != nil {
		return nil, fmt.Errorf("failed creating user: %w", err)
	}
	log.Println("user was created: ", u)
	return u, nil
}

Create Many

pets, err := client.Pet.CreateBulk(
	client.Pet.Create().SetName("pedro").SetOwner(a8m),
	client.Pet.Create().SetName("xabi").SetOwner(a8m),
	client.Pet.Create().SetName("layla").SetOwner(a8m),
).Save(ctx)

names := []string{"pedro", "xabi", "layla"}
pets, err := client.Pet.MapCreateBulk(names, func(c *ent.PetCreate, i int) {
	c.SetName(names[i]).SetOwner(a8m)
}).Save(ctx)

查询实体

func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
	u, err := client.User.
		Query().
		Where(user.NameEQ("a8m")).
		Only(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed querying user: %w", err)
	}
	log.Println("user returned: ", u)
	return u, nil
}

高级使用

指定表明

func (User) Annotations() []schema.Annotation {
	return []schema.Annotation{
		entsql.Annotation{Table: "user"},
	}
}

索引

  • entdemo/ent/schema/user.go 中添加索引
// Indexes 写明索引
func (User) Indexes() []ent.Index {
	return []ent.Index{
		// 非唯一的联合索引
		index.Fields("age", "name"),
		// 非唯一的普通索引
		index.Fields("age"),
		// 唯一索引
		index.Fields("name").Unique(),
	}
}

复用字段

  • entdemo/schema/mixin.go 定义复用字段
package schema

import (
	"time"

	"entgo.io/ent"
	"entgo.io/ent/schema/field"
	"entgo.io/ent/schema/mixin"
)

// -------------------------------------------------
// Mixin definition

// TimeMixin implements the ent.Mixin for sharing
// time fields with package schemas.
type TimeMixin struct {
	// We embed the `mixin.Schema` to avoid
	// implementing the rest of the methods.
	mixin.Schema
}

func (TimeMixin) Fields() []ent.Field {
	return []ent.Field{
		field.Time("created_at").
			Immutable().
			Default(time.Now),
		field.Time("updated_at").
			Default(time.Now).
			UpdateDefault(time.Now),
		field.Bool("deleted").Default(false),
	}
}
// 要记得在User里增加这个方法
func (User) Mixin() []ent.Mixin {
	return []ent.Mixin{
		TimeMixin{},
	}
}

Edge 设置

  • edge 即关系,通过设置 edge 我们可以便捷的进行对象的关联查询
  • 更多参考
// Edges of the User To Car, edge name is: cars
func (User) Edges() []ent.Edge {
	return []ent.Edge{
		edge.To("cars", Car.Type),
	}
}

// Edges of the Car from User, edge name is: owner
func (Car) Edges() []ent.Edge {
	return []ent.Edge{
		// Create an inverse-edge called "owner" of type `User`
		// and reference it to the "cars" edge (in User schema)
		// explicitly using the `Ref` method.
		edge.From("owner", User.Type).
			Ref("cars").
			// setting the edge to unique, ensure
			// that a car can have only one owner.
			Unique(),
	}
}
  • 创建关联
func CreateCarsForUser(ctx context.Context, client *ent.Client, userID int) error {
	user, err := client.User.Get(ctx, userID)
	if err != nil {
		log.Fatalf("failed getting user: %v", err)
		return err
	}

	// 创建一辆新车并关联到用户
	_, err = client.Car.
		Create().
		SetModel("Tesla").
		SetRegisteredAt(time.Now()).
		SetOwner(user).
		Save(ctx)
	if err != nil {
		log.Fatalf("failed creating car for user: %v", err)
		return err
	}

	log.Println("car was created and associated with the user")
	return nil
}
  • 关联查询
func QueryUserCars(ctx context.Context, client *ent.Client, userID int) error {
	user, err := client.User.Get(ctx, userID)
	if err != nil {
		log.Fatalf("failed getting user: %v", err)
		return err
	}

	// 查询用户所拥有的所有汽车
	cars, err := user.QueryCars().All(ctx)
	if err != nil {
		log.Fatalf("failed querying cars: %v", err)
		return err
	}

	for _, car := range cars {
		log.Printf("car: %v, model: %v", car.ID, car.Model)
	}
	return nil
}

Edge 可视化

  • Atlas(是一个通用的数据库迁移工具,可以处理各种数据库的表结构版本管理)可以实现对 Edge 的可视化,Mac 安装:
brew install ariga/tap/atlas

连接池

package main

import (
	"time"

	"<your_project>/ent"

	"entgo.io/ent/dialect/sql"
)

func Open() (*ent.Client, error) {
	drv, err := sql.Open("mysql", "<mysql-dsn>")
	if err != nil {
		return nil, err
	}
	// Get the underlying sql.DB object of the driver.
	db := drv.DB()
	db.SetMaxIdleConns(10)
	db.SetMaxOpenConns(100)
	db.SetConnMaxLifetime(time.Hour)
	return ent.NewClient(ent.Driver(drv)), nil
}

参考

  1. https://github.com/ent/ent
  2. https://entgo.io/
  3. https://pkg.go.dev/entgo.io/ent/dialect/sql
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数