gorm常用

一.CURD相关

创建记录

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

result := db.Create(&user) // 通过数据的指针来创建

user.ID // 返回插入数据的主键

result.Error // 返回

error result.RowsAffected // 返回插入记录的条数

我们还可以使用 Create() 创建多项记录:

users := []*User{

   User{Name: "Jinzhu", Age: 18, Birthday: time.Now()},

   User{Name: "Jackson", Age: 19, Birthday:  time.Now()},

}

result := db.Create(users) // 传递切片以插入多行数据

result.Error // 返回

error result.RowsAffected // 返回插入记录的条数

NOTE 你无法向 ‘create’ 传递结构体,所以你应该传入数据的指针.

用指定的字段创建记录

创建记录并为指定字段赋值。

db.Select("Name", "Age", "CreatedAt").Create(&user)

// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")

创建记录并忽略传递给 ‘Omit’ 的字段值

db.Omit("Name", "Age", "CreatedAt").Create(&user)

// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

批量插入

要高效地插入大量记录,请将切片传递给Create方法。 GORM 将生成一条 SQL 来插入所有数据,以返回所有主键值,并触发 Hook 方法。 当这些记录可以被分割成多个批次时,GORM会开启一个事务</0>来处理它们。

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}

db.Create(&users)

for _, user := range users {

    user.ID // 1,2,3

}

你可以通过db.CreateInBatches方法来指定批量插入的批次大小

var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}} // batch size 100 db.CreateInBatches(users, 100)

创建钩子

GROM允许用户通过实现这些接口 BeforeSaveBeforeCreateAfterSaveAfterCreate来自定义钩子。 这些钩子方法会在创建一条记录时被调用,关于钩子的生命周期请参阅Hooks

注意 在 GORM 中保存、删除操作会默认运行在事务上, 因此在事务完成之前该事务中所作的更改是不可见的,如果您的钩子返回了任何错误,则修改将被回滚

Hook (对象生命周期)是在创建、查询、更新、删除等操作之前、之后调用的函数。

如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。

钩子方法的函数签名应该是 func(*gorm.DB) error

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {

   u.UUID = uuid.New()

  if u.Role == "admin" {

    return errors.New("invalid role")

   }

   return

}

如果你想跳过Hooks方法,可以使用SkipHooks会话模式,例子如下

DB.Session(&gorm.Session{SkipHooks: true}).Create(&user) DB.Session(&gorm.Session{SkipHooks: true}).Create(&users) DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)

根据 Map 创建

GORM支持通过 map[string]interface{} 与 []map[string]interface{}{}来创建记录。

db.Model(&User{}).Create(map[string]interface{}{ "Name": "jinzhu", "Age": 18, })

// batch insert from `[]map[string]interface{}{}`

db.Model(&User{}).Create([]map[string]interface{}{

     {"Name": "jinzhu_1", "Age": 18}, {"Name": "jinzhu_2", "Age": 20},

})

注意当使用map来创建时,钩子方法不会执行,关联不会被保存且不会回写主键。

高级选项

关联创建

创建关联数据时,如果关联值非零,这些关联会被upsert,并且它们的Hooks方法也会被调用。

type CreditCard struct {

   gorm.Model

   Number string

    UserID uint

}

type User struct {

   gorm.Model

   Name string

   CreditCard CreditCard

}

db.Create(&User{ Name: "jinzhu", CreditCard: CreditCard{Number: "411111111111"} })

// INSERT INTO `users` ... // INSERT INTO `credit_cards` ...

你可以通过SelectOmit方法来跳过关联更新,示例如下:

db.Omit("CreditCard").Create(&user) // skip all associations db.Omit(clause.Associations).Create(&user)

默认值

你可以通过结构体Tag default来定义字段的默认值,示例如下:

type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"` }

这些默认值会被当作结构体字段的零值插入到数据库中

注意,当结构体的字段默认值是零值的时候比如 0''false,这些字段值将不会被保存到数据库中,你可以使用指针类型或者Scanner/Valuer来避免这种情况。

type User struct {

  gorm.Model

  Name string

  Age *int `gorm:"default:18"`

  Active sql.NullBool `gorm:"default:true"`

}

注意,若要让字段在数据库中拥有默认值则必须使用defaultTag来为结构体字段设置默认值。如果想要在数据库迁移的时候跳过默认值,可以使用 default:(-),示例如下:

type User struct {

   ID string `gorm:"default:uuid_generate_v3()"` // db func

  FirstName string

  LastName string

  Age uint8

  FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`

}

注意 SQLite 不支持批量插入的时候使用默认值。 前往 SQLite Insert stmt了解。 下面是一个使用案例:

type Pet struct {

 Name string

 `gorm:"default:cat"`

} // 在SqlLite中,这是不允许的, 所以GORM会通过构建错误的SQL来返回错误: // INSERT INTO `pets` (`name`) VALUES ("dog"),(DEFAULT) RETURNING `name` db.Create(&[]Pet{{Name: "dog"}, {}})

一个可行的替代方案是通过钩子方法来设置默认字段

func (p *Pet) BeforeCreate(tx *gorm.DB) (err error) { if p.Name == "" { p.Name = "cat" } }

查询 检索单个对象

GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

  • // 获取第一条记录(主键升序)
    db.First(&user)
    // SELECT * FROM users ORDER BY id LIMIT 1;

    // 获取一条记录,没有指定排序字段
    db.Take(&user)
    // SELECT * FROM users LIMIT 1;

    // 获取最后一条记录(主键降序)
    db.Last(&user)
    // SELECT * FROM users ORDER BY id DESC LIMIT 1;

    result := db.First(&user)
    result.RowsAffected // 返回找到的记录数
    result.Error // returns error or nil

    // 检查 ErrRecordNotFound 错误
    errors.Is(result.Error, gorm.ErrRecordNotFound)

如果你想避免ErrRecordNotFound错误,你可以使用Find,比如

db.Limit(1).Find(&user)Find方法可以接受struct和slice的数据。

对单个对象使用Find而不带limit,db.Find(&user)将会查询整个表并且只返回第一个对象,这是性能不高并且不确定的。

First and Last 方法会按主键排序找到第一条记录和最后一条记录 (分别)。 只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。 例如:

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

var user User
var users []User

// works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// works because model is specified using `db.Model()`
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// doesn't work
result := map[string]interface{}{}
db.Table("users").First(&result)

// works with Take
result := map[string]interface{}{}
db.Table("users").Take(&result)

// no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct {
  Code string
  Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1

检索全部对象

// 获取所有记录 result := db.Find(&users) // SELECT * FROM users; result.RowsAffected // 返回找到的记录数,等于 `len(users)` result.Error // 返回错误

条件

String 条件

// 获取第一条匹配记录
db.Where( "name = ?" , "jinzhu" ).First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// 获取所有匹配的记录
db.Where( "name <> ?" , "jinzhu" ).Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where( "name IN ?" , [] string { "jinzhu" , "jinzhu 2" }).Find(&users)
// SELECT * FROM 用户 WHERE name IN ('jinzhu','jinzhu 2' );

// LIKE
db.Where( "name LIKE ?" , "%jin%" ).Find(&users)
// SELECT * FROM 用户 WHERE name LIKE '%jin%';

// AND
db.Where( "name = ? AND Age >= ?" , "jinzhu" , "22" ).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND Age >= 22;

// 时间
db.Where( "updated_at > ?" , lastWeek).Find(&users)
// SELECT * FROM 用户 WHERE Updated_at > '2000-01-01 00:00:00';

// BETWEEN
db.Where( "created_at BETWEEN ? AND ?" , lastWeek, Today).Find(&users)
// SELECT * FROM 用户 WHEREcreated_at BETWEEN '2000-01-01 00:00:00' AND '2000-01 -08 00:00:00';

如果对象设置了主键,条件查询将不会覆盖主键的值,而是用 And 连接条件。 例如:

var user = User{

 ID: 10

}

db.Where( "id = ?" , 20 ).First(&user)

// SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1

这个查询将会给出record not found错误 所以,在你想要使用例如 user 这样的变量从数据库中获取新值前,需要将例如 id 这样的主键设置为nil。

Struct & Map 条件

// Struct db.Where(&User{Name: "jinzhu" , Age: 20 }).First(&user)

// SELECT * FROM users WHERE name = "jinzhu" AND Age = 20 ORDER BY id LIMIT 1;

// 映射 db.Where( map [ string ] interface {}{ "name" : "jinzhu" , "age" : 20 }).Find(&users)

// SELECT * FROM users WHERE name = "jinzhu" AND Age = 20;

// 主键切片

db.Where([] int64 { 20 , 21 , 22 }).Find(&users)

// SELECT * FROM users WHERE id IN (20, 21, 22);

**注意:**当使用 struct 进行查询时,GORM 只会查询非零字段,这意味着如果您的字段值为0''false其他零值,则不会用于构建查询条件,例如:

db.Where(&User{姓名: "jinzhu" , 年龄: 0 }).Find(&users)

// SELECT * FROM 用户 WHERE name = "jinzhu";

要在查询条件中包含零值,可以使用映射,它将包含所有键值作为查询条件,例如:

db.Where( map [ string ] interface {}{ "Name" : "jinzhu" , "Age" : 0 }).Find(&users)

// SELECT * FROM users WHERE name = "jinzhu" AND Age = 0;

### GORM 使用指南与示例代码解析 #### 1. **GORM 基础概念** GORM 是一款专为 Go 语言设计的强大 ORM 框架,提供全功能的 ORM 支持,包括关联关系、事务处理、嵌套事务等特性[^2]。它的目标是成为开发者友好的工具库,帮助简化数据库交互过程。 --- #### 2. **安装 GORM** 在使用 GORM 之前,需要先通过 `go get` 安装依赖包: ```bash go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite # SQLite 驱动为例 ``` --- #### 3. **初始化 GORM 实例** 以下是一个完整的 GORM 初始化流程,展示了如何连接数据库并创建模型实例[^4]: ```go package main import ( "fmt" "gorm.io/driver/sqlite" "gorm.io/gorm" ) // 定义模型结构体 type User struct { ID uint `gorm:"primaryKey"` Name string `gorm:"size:255"` Email string `gorm:"uniqueIndex;size:255"` CreatedAt time.Time } func main() { // 连接 SQLite 数据库 db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") } // 自动迁移模式 err = db.AutoMigrate(&User{}) if err != nil { fmt.Println(err) return } // 创建记录 user := User{Name: "Alice", Email: "[email protected]"} result := db.Create(&user) if result.Error != nil { fmt.Println(result.Error) } else { fmt.Printf("Created user with ID %d\n", user.ID) } // 查询第一条记录 var firstUser User db.First(&firstUser) fmt.Printf("First user's name is %s\n", firstUser.Name) } ``` --- #### 4. **常用操作** ##### (a) **创建记录** 可以通过 `Create()` 方法向数据库中插入一条新记录[^4]: ```go db.Create(&User{Name: "Bob", Email: "[email protected]"}) ``` ##### (b) **查询记录** 利用 `First()` 或者其他条件查询方法获取指定记录[^4]: ```go var user User db.Where("name = ?", "Alice").First(&user) fmt.Println(user.Email) ``` ##### (c) **更新记录** 支持多种方式更新已有数据[^2]: ```go db.Model(&user).Update("Name", "Charlie") ``` ##### (d) **删除记录** 通过主键或其他唯一字段删除对应的数据项[^2]: ```go db.Delete(&User{}, user.ID) ``` --- #### 5. **高级特性** ##### (a) **分表插件** 对于大规模数据存储需求,可以借助 GORM 的分表插件实现水平扩展[^1]。例如,在初始化过程中加入分表逻辑: ```go plugin := sharding.NewPlugin( sharding.WithKeyFunc(func(model interface{}) string { u := model.(*User) return strconv.Itoa(int(u.ID%10)) // 简单取模算法 }), ) db.Use(plugin) ``` ##### (b) **ClickHouse 插件** 针对特殊场景(如 OLAP 分析),可引入 ClickHouse 插件以增强性能[^3]: ```go type Event struct { Timestamp time.Time `gorm:"column:timestamp"` Value float64 `gorm:"column:value"` } db.Table("events").Create(&Event{Timestamp: time.Now(), Value: 123.45}) ``` --- #### 6. **文档资源** 官方文档提供了详尽的功能说明和技术细节,建议深入阅读以便更好地掌握 GORM 的强大功能: - [GORM 官方文档](https://gorm.io/zh_CN/docs/) - [GitHub 地址](https://github.com/go-gorm/gorm) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值