GORM实践记录 | 豆包MarsCode AI 刷题

103 阅读3分钟

Gorm

安装依赖

首先安装 gorm 框架。其次,需要安装对应数据的驱动,例如 mysql, sqlserver, sqlite.

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

连接数据库

Gorm 通过驱动来连接数据库。首先,编写 DSN 用于表示数据库类型,用户名,密码,数据库地址,数据库名称。接着,通过 gorm.Open() 方法连接数据库,第二个参数表示数据库连接设置。

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

dsn := "username:password@tcp(127.0.0.1:3306)/dbname"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

创建数据

首先定义结构体 Product, 它的属性与数据库中表的字段一一对应。可以通过结构体标记指定数据库表的主键、字段名、默认值等等。

type Product struct {
    ID    uint   `gorm:"primaryKey"`
    Code  string `gorm:"column:code"`
    Price uint   `gorm:"column:price"`
}

然后,通过 Create 语句插入/创建数据。

db.Create(&Product{Code: "D42", Price: 100})

同时,也支持一次插入多条数据,只需要传入数组/切片即可。

products := []*Product{{Code: "123"}, {Code: "asdfasd"}}
db.Create(products)

查询数据

Gorm 可以通过 First(), Find() 两种方法进行查询。它们区别在于 First 只会查询一条数据,而 Find 可以查询多条数据。且,当查询不到数据时, First 会返回报错 ErrRecordNotFound, 而 Find 不会。

其次,当使用结构体作为条件查询时, Gorm 只会查询非零值字段。例如,如果字段值为 0, '', false 或其他零值,该字段不会被用于构建查询条件,应当使用 Map 来构建查询条件。

product := &Product{}
db.First(product) // 查询第一条记录

products := make([]*Product, 0)
db.Where("Price > ?", -1).First(&products) // 查询price大于-1的记录

db.Where(&Product{Code: "123", Price: 0}).Find(&products) // 查询code为123

db.Where(map[string]interface{}{"Code": "123", "Price": 0}).Find(&products) // 查询code 123, price为0

更新数据

同查询一样,使用结构体更新时,只会更新非零值,如果需要更新零值可以使用 Map 更新或使用 Select 选择字段。

db.Model(&Product{ID: 111}).Where("Price = ?", 0).Update("Price", 200)                                   // 更新price为0的记录为200
db.Model(&Product{ID: 111}).Updates(map[string]interface{}{"Price": 200, "Code": "123"})                 // 更新price为200,code为123
db.Model(&Product{ID: 111}).Select("Price").Updates(map[string]interface{}{"Price": 200, "Code": "123"}) // 更新price为200

删除数据

Gorm 在删除数据时支持物理删除和软删除。物理删除就是真正意义上将数据库中的数据记录删除,而软删除并不会真正删除数据记录,而是通过一个额外字段 DeletedAt 记录数据删除时间,但是在通过 Gorm 正常方法查询数据时,无法找到该记录。使用 Unscoped 可以查询到被软删除的数据。

硬删除

type Product struct {
    ID      uint   `gorm:"primaryKey"`
    Code    string `gorm:"column:code"`
    Price   uint   `gorm:"column:price"`
}

db.Delete(&Product{}, 1) // 删除id为1的记录

软删除

type Product struct {
    ID      uint   `gorm:"primaryKey"`
    Code    string `gorm:"column:code"`
    Price   uint   `gorm:"column:price"`
    Deleted gorm.DeletedAt
}


db.Delete(&Product{}, 1) // 删除id为1的记录
products := make([]*Product, 0)
db.Where("ID = ?", 1).Find(&products) // 无法查询到
db.Unscoped().Where("ID = ?", 1).Find(&products) // 可以查询到

事务

Gorm 提供了 Begin, Commit, Rollback 方法用于使用事务。

tx := db.Begin()  // 开始事务
if err = tx.Create(&User{Name: "name"}).Error; err != nil {
    tx.Rollback()  // 回滚
    return
}
tx.Commit()  // 提交事务

但是在实际编程过程中,程序员很容易忘记 commit, rollback. 于是, Gorm 提供了 Transaction 方法用于自动提交事务。

if err = db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&User{Name: "name"}).Error; err != nil {
        return err
    }
    return nil
});

Hook

Gorm 提供了 CRUD 的 Hook 能力。所谓的 Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数。如果任何 Hook 返回错误, Gorm 将停止后续的操作并回滚事务。

func (u *User) BeforeCreate(tx *gorm.DB) (err error){
    if u.Age < 0 {
        return errors.New("can't save invalid data")
    }
    return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    return tx.Create(&Email{ID: u.ID, Email: u.Name + "@***.com"}).Error
}