一、背景和意义
go语言的ent框架是facebook开源的ORM框架,是go语言开发中的常用框架,而关联查询又是日常开发中的常见数据库操作,故文本给出一个使用ent做关联查询的使用示例。
二、引入ent
安装ent的命令为:
go install entgo.io/ent/cmd/ent@latest
找一个空目录作为项目目录,在目录下执行命令创建项目文件:
go mod init entdemo
执行命令之后,项目下添加了go.mod文件。
三、定义数据库实体
接下来创建ent数据库实体文件,一个实体是学生(Student),一个实体是班级(Class),命令为:
ent new Class Student
执行命令之后,项目下多了ent/schema/student.go和ent/schema/class.go两个文件。我们准备修改这两个文件,修改前先添加相关依赖:
go mod tidy
然后修改两个文件的内容,添加字段了关联关系。
ent/schema/student.go:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// Student holds the schema definition for the Student entity.
type Student struct {
ent.Schema
}
// Fields of the Student.
func (Student) Fields() []ent.Field {
return []ent.Field{ // 设置字段信息
field.String("name").MaxLen(50).Comment("名称"),
field.Bool("sex").Comment("性别"),
field.Int("age").Comment("年龄"),
field.Int("class_id").Comment("班级ID"),
}
}
// Edges of the Student.
func (Student) Edges() []ent.Edge {
return []ent.Edge{ // 设置关联关系
edge.From("class", Class.Type).
Ref("student").
Unique().
Field("class_id"). // 通过class_id字段关联class表
Required(),
}
}
ent/schema/class.go:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// Class holds the schema definition for the Class entity.
type Class struct {
ent.Schema
}
// Fields of the Class.
func (Class) Fields() []ent.Field {
return []ent.Field{ // 设置字段信息
field.String("name").MaxLen(50).Comment("名称"),
field.Int("level").Comment("级别"),
}
}
// Edges of the Class.
func (Class) Edges() []ent.Edge {
return []ent.Edge{ // 设置关联关系
edge.To("student", Student.Type), // 表示一个班级可关联多个学生
}
}
其中student与class存在N对1的关联关系,每个学生属于某个班级,一个班级可以包含多个学生,这一关联信息体现在student.go和class.go中的Edges方法中。
接下来执行命令:
go generate ./ent
执行完之后,生成了ent相关的一些模板代码。
三、创建表结构
在项目下创建main.go文件:
package main
import (
"context"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql"
"log"
)
func main() {
// 连接数据库, mysql连接串格式:username:password@(ipAddress)/databaseName?charset=utf8
URL := "test_user:123456@(127.0.0.1)/test?charset=utf8"
client, err := ent.Open("mysql", URL)
if err != nil {
log.Fatalf("连接mysql数据库失败: %v", err)
}
defer client.Close()
// 根据实体类字段配置创建或更新数据库
ctx := context.Background()
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("创建数据结构失败: %v", err)
}
}
该文件中连接mysql数据,然后调用ent.Client.Schama.Create方法创建数据表结构,执行该文件程序之前,mysql数据中是这样:
接下来执行main.go:
go run main.go
执行之后,程序创建了相关的数据表:
四、添加数据
创建create.go文件添加一些数据:
package main
import (
"context"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql"
"log"
)
func main() {
// 连接数据库, mysql连接串格式:username:password@(ipAddress)/databaseName?charset=utf8
URL := "test_user:123456@(127.0.0.1)/test?charset=utf8"
client, err := ent.Open("mysql", URL)
if err != nil {
log.Fatalf("连接mysql数据库失败: %v", err)
}
defer client.Close()
ctx := context.Background()
// 创建班级
class3, err := client.Class.Create().SetName("三班").SetLevel(5).Save(ctx)
if err != nil {
log.Fatalf("创建班级失败:%v", err)
return
}
class2, err := client.Class.Create().SetName("二班").SetLevel(6).Save(ctx)
if err != nil {
log.Fatalf("创建班级失败:%v", err)
return
}
// 创建学生
u1, err := client.Student.Create().
SetClass(class3).SetName("小张").SetSex(false).SetAge(12).Save(ctx)
if err != nil {
log.Fatalf("创建用户失败:%v", err)
return
}
log.Println("创建用户:", u1)
u2, err := client.Student.Create().
SetClass(class3).SetName("小李").SetSex(true).SetAge(11).Save(ctx)
if err != nil {
log.Fatalf("创建用户失败:%v", err)
return
}
log.Println("创建用户:", u2)
u3, err := client.Student.Create().
SetClass(class2).SetName("小赵").SetSex(true).SetAge(12).Save(ctx)
if err != nil {
log.Fatalf("创建用户失败:%v", err)
return
}
log.Println("创建用户:", u3)
}
这里需要注意的是,学生和班级之间存在关联关系,这里是在学生侧调用SetClass方法设置当前学生所关联的班级实体。
接下来执行命令:
go run create.go
执行完之后,查数据库,可以看到students和classes表增加了一些数据。
五、查询数据
创建文件query.go:
package main
import (
"context"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql"
"log"
)
func main() {
// 连接数据库, mysql连接串格式:username:password@(ipAddress)/databaseName?charset=utf8
URL := "test_user:123456@(127.0.0.1)/test?charset=utf8"
client, err := ent.Open("mysql", URL)
if err != nil {
log.Fatalf("连接mysql数据库失败: %v", err)
}
defer client.Close()
ctx := context.Background()
uList, err := client.Student.Query().WithClass().All(ctx)
if err != nil {
log.Fatalf("查询数据失败:%v", err)
return
}
for _, v := range uList {
log.Println("学生:", v, ",所在班级:", v.Edges.Class)
}
}
这里需要注意的是,如果我们既要获取学生数据,又要获取每个学生所在班级的信息,那么需要关联查询students和classes表,代码中是通过调用WithClass()
方法实现的。在做关联查询之后,班级信息会存储在Student实体的Edges.Class
属性中。
运行命令:
go run query.go
终端将输出如下执行结果: