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;

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值