增删改查

1. 插入数据

user := User{
	Username:"zhangsan",
	Password:"123456",
	CreateTime:time.Now().Unix(),
}
db.Create(&user)

user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数
1
2
3
4
5
6
7
8
9
10

1.1 用指定的字段创建:

db.Select("username","password").Create(&user)
1

1.2 忽略字段

db.Omit("username").Create(&user)
1

1.3 批量插入:

var users = []User{{Username: "jinzhu1"}, {Username: "jinzhu2"}, {Username: "jinzhu3"}}
db.Create(&users)

for _, user := range users {
  user.ID // 1,2,3
}
1
2
3
4
5
6

使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:

var users = []User{{Username: "jinzhu_1"}, ...., {Username: "jinzhu_10000"}}

// 数量为 100
db.CreateInBatches(users, 100)
1
2
3
4

1.4 使用map创建:

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},
})
1
2
3
4
5
6
7
8
9

map创建注意,主键不会被填充。

1.5 sql表达式:

DB.Model(&User{}).Create(map[string]interface{}{
		"username": "jinzhu",
		"password": clause.Expr{SQL: "md5(?)", Vars: []interface{}{"123456"}},
	})
1
2
3
4

1.6 使用原生sql语句:

	db.Exec("insert into users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime)

1
2

2. 更新数据

在创建一个表

CREATE TABLE `goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品id',
  `title` varchar(100) NOT NULL COMMENT '商品名',
  `price` decimal(10, 2) NULL DEFAULT 0.00 COMMENT '商品价格',
  `stock` int(11) DEFAULT '0' COMMENT '商品库存',
  `type` int(11) DEFAULT '0' COMMENT '商品类型',
  `create_time` datetime NOT NULL COMMENT '商品创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1
2
3
4
5
6
7
8
9
package dao

import "time"

type Goods struct {
	Id         int
	Title      string
	Price      float64
	Stock      int
	Type       int
	CreateTime time.Time
}

func (v Goods) TableName() string {
	return "goods"
}

func SaveGoods(goods Goods) {
	DB.Create(&goods)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package dao

import (
	"testing"
	"time"
)

func TestSaveGoods(t *testing.T) {
	goods := Goods{
		Title:      "毛巾",
		Price:      25,
		Stock:      100,
		Type:       0,
		CreateTime: time.Now(),
	}
	SaveGoods(goods)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

2.1 保存数据

	goods := Goods{}
	DB.Where("id = ?", 1).Take(&goods)

	goods.Price = 100
	//UPDATE `goods` SET `title`='毛巾',`price`=100.000000,`stock`=100,`type`=0,`create_time  `='2022-11-25 13:03:48' WHERE `id` = 1
	DB.Save(&goods)
1
2
3
4
5
6

2.2 更新单个列

	goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
	DB.Model(&goods).Update("title", "hello")
1
2
3

2.3 更新多列

    goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
    //更新非零值的字段  也可以使用map
	DB.Model(&goods).Updates(Goods{
		Title: "hello",
		Stock: 200,
	})
1
2
3
4
5
6
7

2.4 更新选定的字段

goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
	DB.Model(&goods).Select("title").Updates(Goods{
		Title: "hello",
		Stock: 200,
	})
1
2
3
4
5
6

排除:

goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
	DB.Model(&goods).Omit("title").Updates(Goods{
		Title: "hello",
		Stock: 200,
	})
1
2
3
4
5
6

也可以组合使用

Select("*").Omit("title")
1

gorm更新必须带条件进行更新,否则会返回错误gorm.ErrMissingWhereClause,或者启用 AllowGlobalUpdate 模式

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
1

2.5 表达式

db.Model(&goods).Update("stock", gorm.Expr("stock + 1"))
db.Model(&goods).Update(map[string]interface{}{"stock": gorm.Expr("stock + 1")})
1
2

2.6 子查询更新

  goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
	DB.Model(&goods).Update("title", DB.Model(&User{}).Select("username").Where("id=?", 2))

1
2
3
4

3. 删除数据

	goods := Goods{}
	DB.Where("id = ?", 2).Take(&goods)
	DB.Delete(&goods)
1
2
3
//根据主键删除
DB.Delete(&Goods{}, 1)
1
2

同样的道理,不带条件不能进行删除,必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
// DELETE FROM users
1
2

4. 查询数据

4.1 查询函数

  • Take:查询一条记录

    db.Take(&goods)
    
    1
  • First: 根据主键正序排序后,查询第一条数据

    db.First(&goods)
    
    1
  • Last:根据主键倒序排序后,查询最后一条记录

    db.Last(&goods)
    
    1
  • Find:查询多条记录

    db.Find(&goods)
    
    1
  • Pluck:查询一列值

    var titles []string
    db.Model(&Goods{}).Pluck("title", &titles)
    
    1
    2

当 First、Last、Take 方法找不到记录时,GORM 会返回 ErrRecordNotFound 错误,可以通过对比gorm.ErrRecordNotFound进行判断,或者使用Find和Limit的组合进行查询。

db.Limit(1).Find(&user)
1

4.2 where

通过db.Where函数设置条件

函数说明: db.Where(query interface{}, args ...interface{})

参数说明:

参数名说明
querysql语句的where子句, where子句中使用问号(?)代替参数值,则表示通过args参数绑定参数
argswhere子句绑定的参数,可以绑定多个参数

比如:

db.Where("id in (?)", []int{1,2,5,6}).Take(&goods)
1

4.3 select

设置select子句, 指定返回的字段

var goods Goods
DB.Select("id", "title").Find(&goods)
1
2

也可以写聚合函数

var total int
DB.Model(&Goods{}).Select("count(*) as total").Pluck("total", &total)
fmt.Println(total)
1
2
3

4.4 order

排序

var goods []Goods
	DB.Order("id desc").Find(&goods)
1
2

4.5 分页

通过limit和Offset实现

var goods []Goods
DB.Order("create_time desc").Limit(10).Offset(10).Find(&goods)
1
2

4.6 count

返回查询匹配的行数

var total int64 = 0
DB.Model(Goods{}).Count(&total)
fmt.Println(total)
1
2
3

4.7 分组

//统计每个商品分类下面有多少个商品
//定一个Result结构体类型,用来保存查询结果
type Result struct {
    Type  int
    Total int
}
var results []Result
//等价于: SELECT type, count(*) as  total FROM `goods` GROUP BY type HAVING (total > 0)
db.Model(Goods{}).Select("type, count(*) as  total").Group("type").Having("total > 0").Scan(&results)

//scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名.
//这里因为我们重新定义了一个结构体用于保存结果,但是这个结构体并没有绑定goods表,所以这里只能使用scan查询函数。
1
2
3
4
5
6
7
8
9
10
11
12

Group函数必须搭配Select函数一起使用

4.8 直接执行sql语句

sql := "SELECT type, count(*) as  total FROM `goods` where create_time > ? GROUP BY type HAVING (total > 0)"
//因为sql语句使用了一个问号(?)作为绑定参数, 所以需要传递一个绑定参数(Raw第二个参数).
//Raw函数支持绑定多个参数
db.Raw(sql, "2022-11-06 00:00:00").Scan(&results)
fmt.Println(results)
1
2
3
4
5