使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作 | 青训营

188 阅读9分钟

系列文章目录

Go语言入门指南:基础语法和常用特性解析 | 青训营

高质量编程与性能调优实战 | 青训营

Git 的正确使用姿势与最佳实践:团队协作和版本控制的最佳实践| 青训营

下面是关于 "使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作" 的复习大纲:

@TOC


Prerequisite

0.1 Go 语言基础:了解 Go 语言的基本语法和特性。

0.2 数据库基础:理解关系型数据库的基础概念,例如表、行、列、键等。

0.3 SQL 基础:熟悉基本的 SQL 语句,如 SELECT, INSERT, UPDATE, DELETE 等。

0.4 ORM 概念:了解 ORM(对象关系映射)的基础概念和优点。


Main Curriculum

1.1 介绍 GORM:探讨 GORM 的特点和为什么选择 GORM 作为 Go 的 ORM 工具。

1.2 GORM 安装与配置:学习如何在 Go 项目中安装和配置 GORM。

1.3 数据库连接:使用 GORM 连接到不同的数据库(如 MySQL, PostgreSQL 等)。

1.4 模型定义:如何使用 Go 结构体定义数据库模型。

1.5 CRUD 操作:使用 GORM 进行基本的增删改查操作。

1.6 复杂查询:了解如何使用 GORM 进行复杂的数据库查询。

1.7 关联和联接:学习如何使用 GORM 处理数据库中的关联和联接。

1.8 迁移:使用 GORM 进行数据库迁移。

1.9 优化与性能:探讨如何优化 GORM 查询以提高性能。

1.10 错误处理与日志:了解 GORM 的错误处理机制和如何记录日志。

1.11 总结与实践:回顾所学内容并进行实际的代码实践。


0.2 数据库基础:理解关系型数据库的基础概念,例如表、行、列、键等🌟

关系型数据库是存储和检索数据的最常用方式。它们被称为“关系型”是因为它们以表的形式存储数据,这些表之间有关系。想象一下你的联系人应用或Excel表格。每一行都代表一个联系人,每一列都有特定的信息,如名字、电话号码或地址。

  • :数据的集合,如“联系人”或“订单”。
  • :表中的一个记录。
  • :存储特定类型数据的表的部分,例如“名字”或“地址”。
  • :用于关联不同表的字段,如“订单ID”或“客户ID”。

考虑到关系型数据库的日常应用,它们几乎无处不在。你每天都与它们互动,可能没有意识到。每次你登录到一个网站、购买一个在线商品或查看社交媒体帖子时,你都在与存储在关系型数据库中的数据互动。

关键概念

  1. 表(Table) : 这就像是一个详细的Excel工作表。例如,一个“用户”表可能有ID、姓名、电子邮件、密码等列。
  2. 行(Row) : 这是表中的单个记录。继续上面的例子,“用户”表中的一个行可能是一个具体的用户,例如ID为1,名字为“小明”,电子邮件为“xiaoming@example.com”。
  3. 列(Column) : 这代表数据的某个特定方面。在“用户”表中,姓名和电子邮件都是不同的列。
  4. 键(Key) : 这是关系型数据库中的一个核心概念。主键(PK)是表中的一个特定列,它为表中的每一行提供一个唯一的标识符。而外键(FK)则用于链接两个表。

🤔现在,让我们做个小练习。假设你想在关系型数据库中建立一个关于你所有书的记录。你会如何设计这个数据库?

考虑以下几点:

  • 你需要哪些表?
  • 每个表中会有哪些列?
  • 你会如何使用主键和外键?

🌟

设计书库数据库📚

如果设计一个关系型数据库,可能的方法:

  1. : 至少会有两个表:一个是Books,另一个是Authors

  2. Books表:

    • : BookID (主键), Title, AuthorID (外键), Genre, PublishedDate
    • 描述: 这个表会记录每本书的详细信息。其中,BookID是每本书的唯一标识符,而AuthorID则连接到Authors表,表示这本书的作者。
  3. Authors表:

    • : AuthorID (主键), Name, BirthDate, Nationality
    • 描述: 这个表记录了每位作者的详细信息。AuthorID是每位作者的唯一标识符。
  4. 关键概念:

    • 主键 (PK) : 这是每个表的唯一标识符。在这个例子中,Books表的BookID和Authors表的AuthorID都是主键。
    • 外键 (FK) : 这是一个表中的列,它是另一个表的主键。在这个例子中,Books表的AuthorID是Authors表的外键,它连接两个表,表示哪位作者写了哪本书。

这样的设计允许轻松地查询关于特定书或作者的信息,以及哪位作者写了哪本书。

现在,如果要使用GORM在Go中实现这个设计,需要创建两个结构体来表示这两个表,并使用GORM的特性来建立它们之间的关系。

📝这是一个简化的例子:

type Author struct {
    ID          uint
    Name        string
    BirthDate   time.Time
    Nationality string
    Books       []Book
}

type Book struct {
    ID          uint
    Title       string
    AuthorID    uint
    Genre       string
    PublishedDate time.Time
}

在这个设计中,可以看到GORM如何使用Go的结构体来表示数据库中的表,并使用切片和ID来表示关系。

使用 GORM 对上述书库数据库进行CRUD操作

CRUD代表 增加(Create)读取(Read)更新(Update)删除(Delete) 。这是数据库交互的四个基本操作。

1. 增加(Create)

使用GORM,可以轻松地在数据库中创建新条目。

// 创建一个新的作者
author := Author{Name: "李白", BirthDate: time.Now(), Nationality: "中国"}
db.Create(&author)

// 创建一个新的书籍
book := Book{Title: "静夜思", AuthorID: author.ID, Genre: "古诗", PublishedDate: time.Now()}
db.Create(&book)

2. 读取(Read)

读取是从数据库中检索信息。

// 通过ID获取书籍
var retrievedBook Book
db.First(&retrievedBook, book.ID)

// 获取所有的书籍
var books []Book
db.Find(&books)

// 获取特定作者的所有书籍
var authorBooks []Book
db.Where("author_id = ?", author.ID).Find(&authorBooks)

3. 更新(Update)

更新是修改数据库中的现有条目。

// 更新书籍的标题
db.Model(&book).Update("Title", "望庐山瀑布")

4. 删除(Delete)

删除是从数据库中删除条目。

// 删除一本书
db.Delete(&book)

使用GORM在Go中进行CRUD操作为我们提供了一个非常简洁的方法来与数据库进行交互,而不需要写复杂的SQL语句。

GORM还有许多其他的高级特性和功能,比如事务、复杂的查询、钩子和回调等。

GORM高级特性

  1. 事务 (Transactions)

    在数据库处理中,事务是确保数据完整性的关键。事务是一系列操作,要么全部成功,要么全部失败。GORM提供了简单的方法来处理事务。

    tx := db.Begin()
    
    if err := tx.Create(&user).Error; err != nil {
       tx.Rollback()
       // 处理错误
    }
    
    if err := tx.Create(&profile).Error; err != nil {
       tx.Rollback()
       // 处理错误
    }
    
    tx.Commit()
    

    如果在事务中发生错误,可以使用 Rollback 来撤销事务。如果一切都正常,可以使用 Commit 来提交事务。

  2. 关联 (Associations)

    GORM提供了处理数据库表之间的关系的方法,例如一对一、一对多和多对多关系。

    type User struct {
       ID   uint
       Name string
       Profile Profile
    }
    
    type Profile struct {
       ID     uint
       UserID uint
       Bio    string
    }
    
    var user User
    db.Preload("Profile").Find(&user)
    

    上面的代码会一起加载用户及其相关的个人资料。

    当模型之间有关系时,例如一个用户有多个订单,GORM 使得处理这些关系变得非常容易。

    有以下几种类型的关联:

    1. Belongs To:一个模型属于另一个模型。
    2. Has One:一个模型有一个关联的模型。
    3. Has Many:一个模型有多个关联的模型。
    4. Many To Many:两个模型都有多个关联。

    例如,一个 User 可能有多个 Order

    type User struct {
        gorm.Model
        Orders []Order
    }
    
    type Order struct {
        gorm.Model
        UserID uint
    }
    

    可以使用 GORM 的 Related 方法来获取关联的记录:

    db.Model(&user).Related(&orders)
    
  3. 钩子 (Hooks)

    钩子是在执行数据库操作之前或之后自动触发的函数。这些可以用于数据验证、日志记录等。

    func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
       if u.Name == "" {
          err = errors.New("name cannot be empty")
          return
       }
       return
    }
    

    上面的钩子函数会在创建新用户之前确保用户名不为空。

    又比如可以在保存记录之前自动设置创建日期和修改日期:

    func (u *User) BeforeSave(scope *gorm.Scope) error {
        if u.ID == 0 {
            // 这是一个新记录
            scope.SetColumn("CreatedAt", time.Now())
        } else {
            // 更新记录
            scope.SetColumn("UpdatedAt", time.Now())
        }
        return nil
    }
    
  4. GORM 的回调

    GORM 提供了一种称为回调的功能,允许你在执行某些操作(如保存、更新、查询或删除记录)之前或之后定义自己的逻辑。这是一种非常强大的功能,使你能够实现如记录审计、设置默认值等功能。

    例如,可以为 User 模型定义一个 BeforeSave 回调,该回调在保存用户之前执行:

    func (u *User) BeforeSave(tx *gorm.DB) (err error) {
        if u.Password != "" {
            hashedPassword, err := hashPassword(u.Password)
            if err != nil {
                return err
            }
            u.Password = hashedPassword
        }
        return
    }
    

    在上述示例中,在保存用户之前对密码进行哈希处理。

    回调,就好像你在淘宝买了东西,快递员把东西放到了你家门口的快递柜,然后拨打你之前在淘宝约定好留下的电话,告诉你东西送到了可以出来取。

    钩子,是你跑到路上直接拦住了快递员,把他车上的快递都挨着翻了半天,你做了啥快递员也不知道,有时候还会吧快递弄丢弄坏,甚至快递员都被你带到奇怪的地方去了♂

  5. 查询作用域 (Query Scopes)

    GORM 的另一个高级特性是作用域。作用域允许定义常用的查询逻辑,并在多个查询中重用它。

    例如,可以定义一个 ActiveUsers 作用域来查询所有活跃的用户:

    func ActiveUsers(db *gorm.DB) *gorm.DB {
        return db.Where("active = ?", true)
    }
    
    // 使用作用域进行查询
    db.Scopes(ActiveUsers).Find(&users)
    

    这种方法可以使查询代码更加整洁并减少重复。

  6. 自定义SQL

    虽然GORM提供了很多方便的方法来执行数据库操作,但有时可能需要执行自定义的SQL。GORM也支持这一点。

    db.Raw("SELECT name, age FROM users WHERE name = ?", "李白").Scan(&result)