• docker 生成**
1
docker run --name today -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=pick -e MYSQL_USER=user -e MYSQL_PASSWORD=password -p 3307:3306 -d mysql:latest  

​ 然后返回一堆字符串就是成功

  • Gorm初始化

结构体中的字段必须都为大写,这样子在迁移的时候才会形成相应的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func init() {
// gorm的连接
// 以后具体数据用 config获取
dsn := "user:password@tcp(localhost:3307)/pick?charset=utf8mb4&parseTime=True&loc=Local"
var err error
bb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("gorm.Open err:", err)
return
}
fmt.Println("数据库连接成功")
err1 := bb.AutoMigrate(&User{})
if err1 != nil {
fmt.Println("db.AutoMigrate err:", err1)
return
}
fmt.Println("数据表迁移成功")

Db = &DB{bb}
}
  • Gorm增删改查
  1. 增加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
db.Create(&user)

//增加某些字段
db.select("name").Create(&user)
// 注意
// 1. name 小写,习惯
// 2. 这里只选择创建name,但是age就会空,因此我们需要加入默认值

type User struct{
Name string
Age int `gorm:"type:int;default:18"`
}

//忽略某些字段
db.Omit("age").Create(&user)
// 这里有没有默认值无所谓

// 使用内联条件
DB.First(&User,"name = ?","qm")

// 智能选择字段
type u &UserInfo
DB.Model(&User).First(&u) // 虽然两个结构体不同,但是他会自动填充相应的字段
  1. 修改
1
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
  1. 查看
1
2
3
4
5
6
7
8
9
var users User
fmt.Println("查询所有数据",users)
var firstUser, lastUser, takenUser User
db.First(&firstUser)
fmt.Println("查询第一条数据",firstUser)
db.Last(&lastUser)
fmt.Println("查询最后一条数据",lastUser)
db.Take(&takenUser)
fmt.Println("随便查询一条数据",takenUser)

可以根据主键检索

1
2
db.Find(&user,10)
db.Find(&user,[]int{1,2,3})

可以按照条件检索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var users User
db.Where("id = ?", 1).First(&users)
fmt.Println(users)
var u []User
db.Where("id in (?)", []int{1, 2, 3}).Find(&u)
var uu []User
db.Where("name like ?", "张%").Find(&uu)
var uuu []User
db.Where("name = ? and age = ?", "张三", 18).Find(&uuu)
var uuuu []User
db.Where("id between ? and ?", 1, 3).Find(&uuuu)
var uuuuu []User
db.Where("id > ?", 1).Find(&uuuuu)
fmt.Println(users)
fmt.Println(u)
fmt.Println(uu)
fmt.Println(uuu)
fmt.Println(uuuu)
fmt.Println(uuuuu)

使用或者语义–Or

1
2
var user []User
db.Where("id = ?", 1).Or("id = ?", 2).Find(&user)

使用否定语义–Not

1
2
var user []User
db.Where("id = ?", 1).Not("name = ?", "张三").Find(&user)

使用排序Order

1
2
3
4
5
6
7
// 从上往下升序
var users []User
db.Order("id").Find(&users)

// 从上往下降序
var users []User
db.Order("id desc").Find(&users)

使用分页操作– Limit&&Offset

1
2
3
4
db.Limit(num) --限制几条
db.Limit(-1) --限制取消
db.Offset(n) --从第n+1条开始计数
db.Offset(-1) --从上一条记录的条数开始计数

使用分组&&聚合

1
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)

使用Joins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 可以画画图理解一下,或者直接看mysql的语言
type result struct {
Name string
Email string
}

db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
}

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

  1. 删除
1
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
  • 里面的错误注意

&User 与 &User{}

&User 可以用于引用一个 User 类型的实例的内存地址

&User{} : `&User{}会创建一个User 结构体的新实例,并返回指向该实例的指针。

全局变量和封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type User struct {
gorm.Model
ID int `gorm:"primary_key"`
Name string `gorm:"type:varchar(20);not null"`
Age int `gorm:"type:int;not null"`
}

type DB struct {
*gorm.DB
}

var Db *DB // 不推荐
var Dbb = &DB{} //推荐
var Db DB // 不推荐

func init() {
//........
Db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// .........
Db = &DB{bb} // 很重要
}

​ 推荐使用 &DB{} 的类型,这种类型和第一种其实差不多,但是他会直接创建一个实例,更利于开发

​ init()中,Db虽然一样,但是一个是全局变量,一个是局部变量,而在一个函数之中全局变量级别是低于局部变量的,所以哪怕这边都是一样的Db,但是全局变量的Db如果不赋值,还是Nil,因此最后需要 Db = &DB{bb}

根据唯一索引的操作

  • 在id冲突的情况下 更新列
1
2
3
4
service.G.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"title", "content", "statu"}),
}).Create(ac)
  • 在id冲突下,什么都不做,只插入
1
service.G.Clauses(clause.OnConflict{DoNothing: true}).Create(ac)
  • 在id冲突下,更新指定的列为指定的值
1
2
3
4
service.G.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns(map[string]interface{}{"statu":-1}),
}).Create(ac)
  • 在id冲突下,更新除了主键以外的所有值
1
service.G.Clauses(clause.OnConflict{UpdateAll: true}).Create(ac)

更新

  • Update

    更新单个列

  • Updates

    更新多个列,结构体是零值的不更新

  • Save

    更新全部,零值也算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 更新单个列
db.Model(&User).Update("name":12)

// 更新多个列
db.Model(&User).Updates(map[string]interface{}{"name":"hahah",age:18})
db.Model(&User).UPdates(&user)

// 更新全部(不能与Model一起用)
db.Save(&User)
// 如果想要Save批量更新而不是新增,下面的做法可以借鉴
db.Model(&User).Find(t)
for i , _ :=range t {
t[i] = xx
}
db.Save(t) // Save不常用

// Hook


func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("admin user not allowed to update")
}
return
}

Omit , Select 也能用

错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 通过errors.Is来比较错误代码是否相同
tx := gorm.xxx (tx是一个*gorm.DB的实例)
fmt.println(errors.Is(tx.Error,gorm.ErrRecordNotFound))

var (
// ErrRecordNotFound record not found error
ErrRecordNotFound = logger.ErrRecordNotFound
// ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback`
ErrInvalidTransaction = errors.New("invalid transaction")
// ErrNotImplemented not implemented
ErrNotImplemented = errors.New("not implemented")
// ErrMissingWhereClause missing where clause
ErrMissingWhereClause = errors.New("WHERE conditions required")
// ErrUnsupportedRelation unsupported relations
ErrUnsupportedRelation = errors.New("unsupported relations")
// ErrPrimaryKeyRequired primary keys required
ErrPrimaryKeyRequired = errors.New("primary key required")
// ErrModelValueRequired model value required
ErrModelValueRequired = errors.New("model value required")
// ErrInvalidData unsupported data
ErrInvalidData = errors.New("unsupported data")
// ErrUnsupportedDriver unsupported driver
ErrUnsupportedDriver = errors.New("unsupported driver")
// ErrRegistered registered
ErrRegistered = errors.New("registered")
// ErrInvalidField invalid field
ErrInvalidField = errors.New("invalid field")
// ErrEmptySlice empty slice found
ErrEmptySlice = errors.New("empty slice found")
// ErrDryRunModeUnsupported dry run mode unsupported
ErrDryRunModeUnsupported = errors.New("dry run mode unsupported")
// ErrInvalidDB invalid db
ErrInvalidDB = errors.New("invalid db")
// ErrInvalidValue invalid value
ErrInvalidValue = errors.New("invalid value, should be pointer to struct or slice")
// ErrInvalidValueOfLength invalid values do not match length
ErrInvalidValueOfLength = errors.New("invalid association values, length doesn't match")
// ErrPreloadNotAllowed preload is not allowed when count is used
ErrPreloadNotAllowed = errors.New("preload is not allowed when count is used")
)