4000-520-616
欢迎来到免疫在线!(蚂蚁淘生物旗下平台)  请登录 |  免费注册 |  询价篮
主营:原厂直采,平行进口,授权代理(蚂蚁淘为您服务)
咨询热线电话
4000-520-616
当前位置: 首页 > 新闻动态 >
新闻详情
ent orm笔记4---Code Generation_后场技术-CSDN博客
来自 : CSDN技术社区 发布时间:2021-03-25

在前面几篇文章中 我们经常使用的可能就是entc这个命令了 entc这个工具给带来了很多功能 这篇文章主要整理关于ent orm 中Code Generation

之前的例子中有个知识点少整理了 就是关于如果我们想要看orm在执行过程中详细原生sql语句是可以开启Debug看到的 代码如下

client, err :  ent.Open( mysql ,  root:123456 tcp(10.211.55.3:3306)/graph_traversal?parseTime True ,ent.Debug())
序言 Initialize A New Schema

通过类似如下命令可以生成Schema 模板

entc init User Pet

init 将在ent/schema 目录下创建两个schema user.go 和 pet.go ,如果ent目录不存在 则会创建

Generate Assets

在添加了fields 和 edges 后 可以在项目的根目录运行entc generate 或者使用go generate 生成代码

go generate ./ent

Generate 命令生成以下内容

用于与graph 交互的Client 和Tx对象

schema 的CRUD生成器

每个schema类型的Entity对象

用于与构建交互的常量和断言

SQL方言的migrate 包

Version Compatibility Between entc And ent

这里主要是关于在项目中使用ent 的时候ent的版本要和entc的包的版本相同 并且项目中使用Go modules 进行包管理

Code Generation Options

要了解更多关于 codegen 选项的信息 entc generate -h

generate go code for the schema directoryUsage:  entc generate [flags] pathExamples:  entc generate ./ent/schema  entc generate github.com/a8m/xFlags:      --header string                           override codegen header  -h, --help                                    help for generate      --idtype [int int64 uint uint64 string]   type of the id field (default int)      --storage string                          storage driver to support in codegen (default  sql )      --target string                           target directory for codegen      --template strings                        external templates to execute
Storage

entc 可以为 SQL 和 Gremlin 方言生成资产。

External Templates

接受要执行的外部 Go 模板。如果模板名称已经由 entc 定义 它将覆盖现有的名称。否则 它将把执行输出写入与模板同名的文件。Flag 格式支持如下文件、目录和 glob:

entc generate --template  dir-path  --template glob path/to/*.tmpl  ./ent/schema

更多的信息和例子可以在外部模板文档中找到

Use entc As A Package

运行 entc 的另一个选项是将其作为一个包使用 如下所示:

package mainimport (     log      github.com/facebook/ent/entc      github.com/facebook/ent/entc/gen      github.com/facebook/ent/schema/field )func main() {    err :  entc.Generate( ./schema ,  gen.Config{        Header:  // Your Custom Header ,        IDType:  field.TypeInfo{Type: field.TypeInt},    })    if err !  nil {        log.Fatal( running ent codegen: , err)    }}
Schema Description

如果想要得到我们定义的schema的描述信息 可以通过如下命令

entc describe ./ent/schema

以之前的例子中执行效果如下

User:         ------- -------- -------- ---------- ---------- --------- --------------- ----------- ----------------------- ------------         | Field |  Type  | Unique | Optional | Nillable | Default | UpdateDefault | Immutable |       StructTag       | Validators |         ------- -------- -------- ---------- ---------- --------- --------------- ----------- ----------------------- ------------         | id    |  nil   | false  | false    | false    | false   | false         | false     | json: id,omitempty    |          0 |        | name  | string | false  | false    | false    | false   | false         | false     | json: name,omitempty  |          0 |         ------- -------- -------- ---------- ---------- --------- --------------- ----------- ----------------------- ------------          ----------- ------ --------- ----------- ---------- -------- ----------         |   Edge    | Type | Inverse |  BackRef  | Relation | Unique | Optional |         ----------- ------ --------- ----------- ---------- -------- ----------         | followers | User | true    | following | M2M      | false  | true     |        | following | User | false   |           | M2M      | false  | true     |         ----------- ------ --------- ----------- ---------- -------- ---------- 
CRUD API Create A New Client

「MySQL」

package mainimport (     log      project /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.Fatal(err)    }    defer client.Close()}

「PostgreSQL」

package mainimport (     log      project /ent     _  github.com/lib/pq )func main() {    client, err :  ent.Open( postgres , host host  port port  user user  dbname database  password pass )    if err !  nil {        log.Fatal(err)    }    defer client.Close()}

「SQLite」

package mainimport (     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.Fatal(err)    }    defer client.Close()}

「Gremlin (AWS Neptune)」

package mainimport (     log      project /ent )func main() {    client, err :  ent.Open( gremlin ,  http://localhost:8182 )    if err !  nil {        log.Fatal(err)    }}
Create An Entity

「Save」 a user.

a8m, err :  client.User.    // UserClient.    Create().               // User create builder.    SetName( a8m ).         // Set field value.    SetNillableAge(age).    // Avoid nil checks.    AddGroups(g1, g2).      // Add many edges.    SetSpouse(nati).        // Set unique edge.    Save(ctx)               // Create and return.

「SaveX」 a pet; Unlike 「Save」, 「SaveX」 panics if an error occurs.

pedro :  client.Pet.    // PetClient.    Create().           // Pet create builder.    SetName( pedro ).   // Set field value.    SetOwner(a8m).      // Set owner (unique edge).    SaveX(ctx)          // Create and return.
Create Many

「Save」 a bulk of pets

names :  []string{ pedro ,  xabi ,  layla }bulk :  make([]*ent.PetCreate, len(names))for i, name :  range names {    bulk[i]   client.Pet.Create().SetName(name).SetOwner(a8m)}pets, err :  client.Pet.CreateBulk(bulk...).Save(ctx)
Update One

更新一个从数据库返回的entity

a8m, err   a8m.Update().    // User update builder.    RemoveGroup(g2).        // Remove specific edge.    ClearCard().            // Clear unique edge.    SetAge(30).             // Set field value    Save(ctx)               // Save and return.
Update By ID
pedro, err :  client.Pet.   // PetClient.    UpdateOneID(id).        // Pet update builder.    SetName( pedro ).       // Set field name.    SetOwnerID(owner).      // Set unique edge, using id.    Save(ctx)               // Save and return.
Update Many

以断言进行过滤

n, err :  client.User.          // UserClient.    Update().                   // Pet update builder.    Where(                      //        user.Or(                // (age   30 OR name    bar )             user.AgeEQ(30),     //            user.Name( bar ),   // AND        ),                      //          user.HasFollowers(),    // UserHasFollowers()      ).                          //    SetName( foo ).             // Set field name.    Save(ctx)                   // exec and return.

通过edge 断言进行查询

n, err :  client.User.          // UserClient.    Update().                   // Pet update builder.    Where(                      //         user.HasFriendsWith(    // UserHasFriendsWith (            user.Or(            //   age   20                user.Age(20),   //      OR                user.Age(30),   //   age   30            )                   // )        ),                      //    ).                          //    SetName( a8m ).             // Set field name.    Save(ctx)                   // exec and return.
Query The Graph

获取所有用户的关注者

users, err :  client.User.      // UserClient.    Query().                    // User query builder.    Where(user.HasFollowers()). // filter only users with followers.    All(ctx)                    // query and return.

获取特定用户的所有跟随者; 从graph中的一个节点开始遍历

users, err :  a8m.    QueryFollowers().    All(ctx)

获取所有宠物的名字

names, err :  client.Pet.    Query().    Select(pet.FieldName).    Strings(ctx)

获取所有宠物的名字和年龄

var v []struct {    Age  int     json: age     Name string  json: name }err :  client.Pet.    Query().    Select(pet.FieldAge, pet.FieldName).    Scan(ctx,  v)if err !  nil {    log.Fatal(err)}
Delete One

这个用于如果我们已经通过client查询到了一个entity 然后想要删除这条记录

err :  client.User.    DeleteOne(a8m).    Exec(ctx)

Delete by ID.

err :  client.User.    DeleteOneID(id).    Exec(ctx)
Delete Many

使用断言进行删除

err :  client.File.    Delete().    Where(file.UpdatedAtLT(date))    Exec(ctx)
Mutation

通过 entc init 生成的每个schema 都有自己的mutaion 例如我们通过 entc init User Pet, 在通过go generate ./ent 生成的代码中有 ent/mutation.go

在该文件中定义了

.....// UserMutation represents an operation that mutate the Users// nodes in the graph.type UserMutation struct { config op            Op typ           string id            *int name          *string age           *int addage        *int clearedFields map[string]struct{} done          bool oldValue      func(context.Context) (*User, error)}.....// PetMutation represents an operation that mutate the Pets// nodes in the graph.type PetMutation struct { config op            Op typ           string id            *int name          *string age           *int addage        *int clearedFields map[string]struct{} done          bool oldValue      func(context.Context) (*Pet, error)}

例如 所有的User builders都共享相同的UserMutaion 对象 左右的builder 类型都继承通用的ent.Mutation接口.

这里所说的 user builders 拿User schema来说指的是UserCreate、UserDelete、UserQuery、UserUpdate 对象 go generate 生成的代码中 我们可以到

./ent/user_create.go、./ent/user_delete.go、./ent/user_query.go、./ent/user_update.go文件中看到如下定义

// ./ent/user_create.go// UserCreate is the builder for creating a User entity.type UserCreate struct { config mutation *UserMutation hooks    []Hook}//./ent/user_delete.go// UserDelete is the builder for deleting a User entity.type UserDelete struct { config hooks      []Hook mutation   *UserMutation predicates []predicate.User}// ./ent/user_query.go// UserQuery is the builder for querying User entities.type UserQuery struct { config limit      *int offset     *int order      []OrderFunc unique     []string predicates []predicate.User // intermediate query (i.e. traversal path). sql  *sql.Selector path func(context.Context) (*sql.Selector, error)}// ./ent/user_update.go// UserUpdate is the builder for updating User entities.type UserUpdate struct { config hooks      []Hook mutation   *UserMutation predicates []predicate.User}

在下面的例子中 ent.UserCreate 和 ent.UserUpdate 都使用一个通用的方法对age 和name 列进行操作

package mainimport (    context     log    _  github.com/go-sql-driver/mysql     github.com/peanut-cc/ent_orm_notes/aboutMutaion/ent )func main() {   client, err :  ent.Open( mysql ,  root:123456 tcp(10.211.55.3:3306)/aboutMutaion?parseTime True )   if err !  nil {      log.Fatal(err)   }   defer client.Close()   ctx :  context.Background()   // run the auto migration tool   if err :  client.Schema.Create(ctx); err !  nil {      log.Fatalf( failed creating schema resources:%v , err)   }   Do(ctx, client)}func Do(ctx context.Context, client *ent.Client) {   creator :  client.User.Create()   SetAgeName(creator.Mutation())   creator.SaveX(ctx)   updater :  client.User.UpdateOneID(1)   SetAgeName(updater.Mutation())   updater.SaveX(ctx)}// SetAgeName sets the age and the name for any mutation.func SetAgeName(m *ent.UserMutation) {   m.SetAge(32)   m.SetName( Ariel )}

在某些情况下 你希望对多个不同的类型应用同一个方法 对于这种情况 要么使用通用的ent.Mutation 接口 或者自己实现一个接口 代码如下

func Do2(ctx context.Context, client *ent.Client) {   creator1 :  client.User.Create().SetAge(18)   SetName(creator1.Mutation(),  a8m )   creator1.SaveX(ctx)   creator2 :  client.Pet.Create().SetAge(16)   SetName(creator2.Mutation(),  pedro )   creator2.SaveX(ctx)}// SetNamer wraps the 2 methods for getting// and setting the  name  field in mutations.type SetNamer interface {   SetName(string)   Name() (string, bool)}func SetName(m SetNamer, name string) {   if _, exist :  m.Name(); !exist {      m.SetName(name)   }}
Graph Traversal

在这个部分的例子中会使用如下的Graph

er-traversal-graph

er-traversal-graph-gopher

上面的遍历从一个 Group 实体开始 继续到它的 admin (edge) 继续到它的朋友(edge) 获取他们的宠物(edge) 获取每个宠物的朋友(edge) 并请求它们的主人

func Traverse(ctx context.Context, client *ent.Client) error {    owner, err :  client.Group.         // GroupClient.        Query().                        // Query builder.        Where(group.Name( Github )).    // Filter only Github group (only 1).        QueryAdmin().                   // Getting Dan.        QueryFriends().                 // Getting Dan s friends: [Ariel].        QueryPets().                    // Their pets: [Pedro, Xabi].        QueryFriends().                 // Pedro s friends: [Coco], Xabi s friends: [].        QueryOwner().                   // Coco s owner: Alex.        Only(ctx)                       // Expect only one entity to return in the query.    if err !  nil {        return fmt.Errorf( failed querying the owner: %v , err)    }    fmt.Println(owner)    // Output:    // User(id 3, age 37, name Alex)    return nil}

下面的遍历如何

er-traversal-graph-gopher-query

我们希望得到所有宠物(entities)的所有者(edge)是朋友(edge)的一些群管理员(edge)。

func Traverse2(ctx context.Context, client *ent.Client) error { pets, err :  client.Pet.  Query().  Where(   pet.HasOwnerWith(    user.HasFriendsWith(     user.HasManage(),    ),   ),  ).  All(ctx) if err !  nil {  return fmt.Errorf( failed querying the pets: %v , err) } fmt.Println(pets) // Output: // [Pet(id 1, name Pedro) Pet(id 2, name Xabi)] return nil}

上面的查询中 查询所有的宠物 条件是: 宠物要有主人 同时宠物的主人是要有朋友 同时该主人还要属于管理员

Eager Loading

ent 支持通过它们的edges 查询 并将关联的entities 添加到返回的对象中

通过下面的例子理解

er-group-users

查询上面关系中所有用户和它们的宠物 代码如下

func edgerLoading(ctx context.Context, client *ent.Client) {   users, err :  client.User.Query().WithPets().All(ctx)   if err !  nil {      log.Fatalf( user query failed:%v , err)   }   log.Println(users)   for _, u :  range users {      for _, p :  range u.Edges.Pets {         log.Printf( user (%v) --   Pet (%v)\\n , u.Name, p.Name)      }   }}

完整的代码在 https://github.com/peanut-cc/ent_orm_notes/graph_traversal

查询的结果如下

2020/09/01 20:09:07 [User(id 1, age 29, name Dan) User(id 2, age 30, name Ariel) User(id 3, age 37, name Alex) User(id 4, age 18, name peanut)]2020/09/01 20:09:07 user (Ariel) --   Pet (Pedro)2020/09/01 20:09:07 user (Ariel) --   Pet (Xabi)2020/09/01 20:09:07 user (Alex) --   Pet (Coco)

预加载允许查询多个关联 包括嵌套关联 还可以过滤 排序或限制查询结果 例如

func edgerLoading2(ctx context.Context, client *ent.Client) {   users, err :  client.User.      Query().      Where(         user.AgeGT(18),      ).      WithPets().      WithGroups(func(q *ent.GroupQuery) {         q.Limit(5)         q.WithUsers().Limit(5)      }).All(ctx)   if err !  nil {      log.Fatalf( user query failed:%v , err)   }   log.Println(users)   for _, u :  range users {      for _, p :  range u.Edges.Pets {         log.Printf( user (%v) --  Pet (%v)\\n , u.Name, p.Name)      }      for _, g :  range u.Edges.Groups {         log.Printf( user (%v) -- Group (%v)\\n , u.Name, g.Name)      }   }}

每个query-builder都有一个方法列表 其形式为 With E (...func( N Query))

E 代表边缘名称(像WithGroups) N 代表边缘类型(像GroupQuery)。

注意 只有 SQL 方言支持这个特性

Aggregation Group By

按所有用户的姓名和年龄字段分组 并计算其总年龄。

package mainimport (  context   log   github.com/peanut-cc/ent_orm_notes/groupBy/ent/user  _  github.com/go-sql-driver/mysql   github.com/peanut-cc/ent_orm_notes/groupBy/ent )func main() { client, err :  ent.Open( mysql ,  root:123456 tcp(10.211.55.3:3306)/groupBy?parseTime True ,  ent.Debug()) if err !  nil {  log.Fatal(err) } defer client.Close() ctx :  context.Background() // run the auto migration tool if err :  client.Schema.Create(ctx); err !  nil {  log.Fatalf( failed creating schema resources:%v , err) } GenData(ctx, client) Do(ctx, client)}func GenData(ctx context.Context, client *ent.Client) { client.User.Create().SetName( peanut ).SetAge(18).SaveX(ctx) client.User.Create().SetName( jack ).SetAge(20).SaveX(ctx) client.User.Create().SetName( steve ).SetAge(22).SaveX(ctx) client.User.Create().SetName( peanut-cc ).SetAge(18).SaveX(ctx) client.User.Create().SetName( jack-dd ).SetAge(18).SaveX(ctx)}func Do(ctx context.Context, client *ent.Client) { var v []struct {  Name  string  json: name   Age   int     json: age   Sum   int     json: sum   Count int     json: count  } client.User.  Query().  GroupBy(   user.FieldName, user.FieldAge,  ).  Aggregate(   ent.Count(),   ent.Sum(user.FieldAge),  ).  ScanX(ctx,  v) log.Println(v)}

按一个字段分组 例子如下

func Do2(ctx context.Context, client *ent.Client) { names :  client.User.Query().GroupBy(user.FieldName).StringsX(ctx) log.Println(names)}
Predicates Field Predicates

Bool:

, !

Numberic:

, ! , , , , ,

IN, NOT IN

Time:

, ! , , , ,

IN, NOT IN

String:

, ! , , , ,

IN, NOT IN

Contains, HasPrefix, HasSuffix

ContainsFold, EqualFold (「SQL」 specific)

Optional fields:

IsNil, NotNil

Edge Predicates

「HasEdge」 例如 查询所有宠物的所有者 使用:

 client.Pet.      Query().      Where(pet.HasOwner()).      All(ctx)

「HasEdgeWith」

 client.Pet.      Query().      Where(pet.HasOwnerWith(user.Name( a8m ))).      All(ctx)
Negation (NOT)
client.Pet.    Query().    Where(pet.Not(pet.NameHasPrefix( Ari ))).    All(ctx)
Disjunction (OR)
client.Pet.    Query().    Where(        pet.Or(            pet.HasOwner(),            pet.Not(pet.HasFriends()),        )    ).    All(ctx)
Conjunction (AND)
client.Pet.    Query().    Where(        pet.And(            pet.HasOwner(),            pet.Not(pet.HasFriends()),        )    ).    All(ctx)
Custom Predicates

如果想编写自己的特定方言的逻辑 Custom predicates可能很有用。

pets :  client.Pet.    Query().    Where(predicate.Pet(func(s *sql.Selector) {        s.Where(sql.InInts(pet.OwnerColumn, 1, 2, 3))    })).    AllX(ctx)
Paging And Ordering Limit

将查询结果限制为 n 个实体。

users, err :  client.User.    Query().    Limit(n).    All(ctx)
Offset

设置从查询返回的第一个最大数量。

users, err :  client.User.    Query().    Offset(10).    All(ctx)
Ordering

Order 返回按一个或多个字段的值排序的实体。

users, err :  client.User.Query().    Order(ent.Asc(user.FieldName)).    All(ctx)
延伸阅读

https://entgo.io/

\"\" \"\" \"\" 点赞 \"\" \"\" 评论

本文链接: http://qddschemau.immuno-online.com/view-750150.html

发布于 : 2021-03-25 阅读(0)
公司介绍
品牌分类
食品添加剂 Others
联络我们
服务热线:4000-520-616
(限工作日9:00-18:00)
QQ :1570468124
手机:18915418616
官网:http://