一架梯子,一头程序猿,仰望星空!
Go Ent ORM框架教程 > 内容正文

实体操作


1. ent实体操作简介

本教程将全面指导您掌握ent框架的实体操作,涵盖实体的创建、查询、更新、删除实体的全过程。适合初学者逐步深入学习ent的核心功能。

3. 实体创建操作

3.1 创建单个实体

创建实体是数据持久化的基础操作。以下是如何使用 ent 框架创建单个实体对象并将其保存到数据库中的步骤:

  1. 首先,定义一个实体的结构和字段。即在 schema 文件中定义实体的模型。
  2. 运行 ent generate 命令生成相应的实体操作代码。
  3. 使用生成的 Create 方法来构建新的实体,通过链式调用可以设置实体的字段值。
  4. 最后,调用 Save 方法将实体保存到数据库中。

以下是一个示例,展示了如何创建并保存一个用户实体:

package main

import (
    "context"
    "log"
    "entdemo/ent"
)

func main() {
    // 创建 Client 实例进行数据库交互
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("打开数据库连接失败: %v", err)
    }
    defer client.Close()

    // 创建上下文
    ctx := context.Background()
    
    // 使用Client创建一个用户实体
    a8m, err := client.User.    // UserClient.
        Create().               // 创建一个User实体
        SetName("a8m").         // 设置用户名为 "a8m"
        Save(ctx)               // 保存实体到数据库
    if err != nil {
        log.Fatalf("创建用户实体失败: %v", err)
    }

    // 实体成功保存到数据库
    log.Printf("用户实体已保存: %v", a8m)
}

该示例首先创建了数据库客户端 client,然后使用 User.Create 方法来设定新用户的属性,最后调用 Save 方法将用户保存到数据库。

3.2 批量创建实体

在某些场景下,我们可能需要创建多个实体,例如,在初始化数据库时,或者是数据批量导入操作。ent 框架提供了批量创建实体的能力,这比单个创建实体后保存性能更优。

批量创建实体操作的步骤如下:

  1. 使用 CreateBulk 方法替代 Create 方法,它允许在一个操作中创建多个实体。
  2. 为每个要创建的实体调用 Create
  3. 完成所有实体的创建后,使用 Save 方法批量保存实体到数据库中。

以下是一个批量创建实体的示例:

package main

import (
    "context"
    "log"
    "entdemo/ent"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("打开数据库连接失败: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // 批量创建Pet实体
    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)
    if err != nil {
        log.Fatalf("批量创建Pet实体失败: %v", err)
    }

    log.Printf("批量创建了%d个Pet实体\n", len(pets))
}

此示例操作先创建了一个 client 客户端,然后通过 CreateBulk 方法构建了多个 Pet 实体并设置了它们的名称和拥有者字段。所有实体都会在 Save 调用时一次性保存到数据库中,这种批处理方式在处理大量数据时性能较好。

4. 实体查询操作

4.1 基本查询

数据库的查询操作是获取信息的基础方式。在 ent 中,可以使用 Query 方法来开始一个查询。以下是一些基础查询实体的步骤和示例:

  1. 确保你已经有了可用的 Client 实例。
  2. 使用 Client.Query() 或者实体辅助方法如 Pet.Query() 来创建一个查询。
  3. 根据需要添加过滤条件,如 Where
  4. 通过调用 All 方法来执行查询并获得结果。
package main

import (
    "context"
    "log"
    "entdemo/ent"
    "entdemo/ent/user"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("打开数据库连接失败: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // 查询所有名为"a8m"的用户
    users, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).
        All(ctx)
    if err != nil {
        log.Fatalf("查询用户失败: %v", err)
    }

    for _, u := range users {
        log.Printf("找到用户: %#v\n", u)
    }
}

该示例展示了如何查找所有用户名为 "a8m" 的用户。

4.2 分页和排序

分页和排序是查询时常用到的高级功能,用于控制数据的输出顺序和数量。以下是如何利用 ent 实现分页和排序查询:

  1. 使用 Limit 方法设置返回结果的最大数量。
  2. 使用 Offset 方法跳过前面的一些结果。
  3. 使用 Order 方法指定排序的字段和方向。

下面是一个分页和排序查询的示例:

package main

import (
    "context"
    "log"
    "entdemo/ent"
    "entdemo/ent/pet"
)

func main() {
    client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("打开数据库连接失败: %v", err)
    }
    defer client.Close()
    ctx := context.Background()

    // 按照年龄降序,分页查询Pet
    pets, err := client.Pet.
        Query().
        Order(ent.Desc(pet.FieldAge)).
        Limit(10).
        Offset(0).
        All(ctx)
    if err != nil {
        log.Fatalf("查询Pet失败: %v", err)
    }

    for _, p := range pets {
        log.Printf("找到Pet: %#v\n", p)
    }
}

此示例演示了如何获取第一页,最多10条记录,并按宠物的年龄降序排序的宠物数据集。通过修改 LimitOffset 的值,可以实现遍历整个数据集的分页功能。

5. 实体更新操作

5.1 更新单个实体

在许多应用中,更新实体是日常操作的重要部分。在本节中,我们将展示如何使用 Ent 框架更新数据库中的单个实体。

首先,假设我们需要更新一个用户的年龄,我们可以利用由 Ent 自动生成的 Update 方法来实现。

// 假设我们已经有了一个用户实体 `a8m` 和上下文 `ctx`

a8m, err := a8m.Update().         // 创建一个用户更新构建器
    SetAge(30).                   // 设置用户的年龄为30岁
    Save(ctx)                     // 执行保存操作并返回结果
if err != nil {
    log.Fatalf("更新用户失败: %v", err)
}

您也可以同时更新多个字段:

a8m, err := a8m.Update().
    SetAge(30).                    // 更新年龄
    SetName("Ariel").              // 更新名字
    AddRank(10).                   // 将排名增加10
    Save(ctx)
if err != nil {
    log.Fatalf("更新用户失败: %v", err)
}

更新操作可以链式调用,这非常灵活且容易阅读。调用 Save 方法将更新被调用,并返回更新后的实体或错误信息。

5.2 条件更新

Ent 允许您根据条件执行更新操作。以下是一个例子,只有满足特定条件的用户才会被更新。

// 假设我们有一个用户的 `id` 和我们希望将该用户标记为完成的版本 `currentVersion`

err := client.Todo.
    UpdateOneID(id).          // 创建一个通过用户ID进行更新的构建器
    SetStatus(todo.StatusDone).
    AddVersion(1).
    Where(
        todo.Version(currentVersion),  // 更新操作只有在当前版本号匹配时才会执行
    ).
    Exec(ctx)
switch {
case ent.IsNotFound(err):
    fmt.Println("待办事项未找到")
case err != nil:
    fmt.Println("更新错误:", err)
}

使用条件更新时,必须涉及 .Where() 方法。这使您可以根据当前数据库中的值判断是否应该执行更新,这对于确保数据的一致性和完整性至关重要。

6. 实体删除操作

6.1 删除单个实体

删除实体是数据库操作中另一项重要功能。Ent 框架提供了简单的API来执行删除操作。

以下示例演示了如何删除一个给定的用户实体:

err := client.User.
    DeleteOne(a8m).  // 创建用户删除构建器
    Exec(ctx)        // 执行删除操作
if err != nil {
    log.Fatalf("删除用户失败: %v", err)
}

6.2 条件删除

类似于更新操作,我们也可以基于特定的条件来执行删除操作。在某些场景下,当实体满足特定条件时我们才希望将其删除。使用 .Where() 方法可以定义这些条件:

// 假设我们希望删除更新时间小于某个日期的所有文件

affected, err := client.File.
    Delete().
    Where(file.UpdatedAtLT(date)).  // 只有在文件的更新时间小于给定日期时才执行删除
    Exec(ctx)
if err != nil {
    log.Fatalf("删除文件失败: %v", err)
}

// 此操作返回受删除操作影响的记录数量
fmt.Printf("有 %d 个文件被删除\n", affected)

使用条件删除操作可以确保我们的数据操作精确控制,只有真正满足条件的实体才会被删除。这提高了数据库操作的安全性和可靠性。