GO笔记04 - GORM

GORM介绍

gorm是一个使用Go语言编写的ORM框架。它文档齐全,对开发者友好,支持主流数据库。
ORM:Object Relation Mapping 对象关系映射 优点:提高开发效率
缺点:牺牲执行性能,灵活性
安装GORM
go get -u github.com/jinzhu/gorm

Docker快速创建Mysql实例 在本地的33060端口运行一个名为mysql,用户名root密码123456的MySQL容器环境:
docker run --name mysql -p 33060:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.19

> docker exec -it mysql bash > mysql -uroot -p > ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY '123456';

下载Sequel Pro Nightly(正式版连接Mysql8.0会崩溃)
https://sequelpro.com/test-bu...
创建数据库db_test
CREATE DATABASE db_test;

连接Mysql
import ( "gorm.io/driver/mysql" "gorm.io/gorm" )func main() { dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) }

GORM操作Mysql
type User struct { IDuint Name string }func main() { dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local" db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) //自动建表 db.AutoMigrate(&User{}) //创建数据 u1 := User{1, "AAA"} u2 := User{2, "BBB"} db.Create(&u1) db.Create(&u2) // 删除 db.Delete(&u2) // 更新 db.Model(&u1).Update("name", "AAA1") // 查询单条 var u User db.First(&u) fmt.Printf("%#v\n", u) //{ID:0x1, Name:"AAA1"} // 查询多条 var us []User db.Find(&us) fmt.Printf("%#v\n", us) }

GORM Model
gorm.Model
GORM内置了一个 gorm.Model结构体。包含 ID, CreatedAt, UpdatedAt, DeletedAt四个字段。
type Model struct { IDuint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time }

模型嵌套
// 将 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中 type User struct { gorm.Model Name string }

自定义模型
// 不使用gorm.Model,自定义模型 type User struct { IDint Name string }

模型定义示例
type User struct { gorm.Model Namestring Agesql.NullInt64 //零值类型 Birthday*time.Time `gorm:"colume:birth"` //自定义字段名 Emailstring`gorm:"type:varchar(100); unique_index"` Rolestring`gorm:"size:255"` // 设置字段大小为255 MemberNumber *string `gorm:"unique; not null"` // 设置会员号(member number)唯一并且不为空 Numint`gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型 Addressstring`gorm:"index:addr"` // 给address字段创建名为addr的索引 IgnoreMeint`gorm:"-"` // 忽略本字段 }

模型使用 表名 前缀&&表名单复数
func main() { dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local" db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{ NamingStrategy: schema.NamingStrategy{ TablePrefix:"t_", // 表名前缀,`User` 的表名应该是 `t_users` SingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `t_user` }, }) db.AutoMigrate(&User{})//创建t_user表 }

使用TableName()重写表名
type User struct { gorm.Model Name string }// TableName 会将 User 的表名重写为 `profiles` func (User) TableName() string { return "profiles" }

临时指定表名
// 根据 User 的字段创建 `deleted_users` 表 db.Table("deleted_users").AutoMigrate(&User{})var deleted_users []User db.Table("deleted_users").Find(&deleted_users) //// SELECT * FROM deleted_users;

列名 结构体tag指定列名
type Animal struct { AnimalIdint64`gorm:"column:beast_id"` Birthdaytime.Time `gorm:"column:day_of_the_beast"` Ageint64`gorm:"column:age_of_the_beast"` }

时间戳 CreatedAt
// 可以使用`Update`方法来改变`CreateAt`的值 db.Model(&user).Update("CreatedAt", time.Now())

UpdatedAt
db.Save(&user) // `UpdatedAt`将会是当前时间 db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间

DeletedAt
如果模型有DeletedAt字段,调用Delete删除该记录时,将会设置DeletedAt字段为当前时间,而不是直接将记录从数据库中删除。
字段默认值
type User struct { IDint64 Name string `gorm:"default:'XXX'"` Ageint64 }func main() { dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local" db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) db.AutoMigrate(&User{}) u1 := User{Age: 1} db.Debug().Create(&u1)//INSERT INTO `users` (`name`,`age`) VALUES ('XXX',1) u2 := User{Name:"", Age: 2} db.Debug().Create(&u2)//INSERT INTO `users` (`name`,`age`) VALUES ('XXX',1) }

注意:通过tag创建的默认值,创建记录时SQL会排除没有值或值为零值(0,"",false等)的字段。
写入零值
  1. 通过指针
    type User struct { IDint64 Name *string `gorm:"default:'XXX'"` Ageint64 }func main() { ... u1 := User{Name: new(string), Age: 1} db.Debug().Create(&u1) //INSERT INTO `users` (`name`,`age`) VALUES ('',1) }

  2. 实现Scanner/Valuer接口
    type User struct { IDint64 Name sql.NullString `gorm:"default:'XXX'"` Ageint64 }func main() { ... u1 := User{Name: sql.NullString{String: "", Valid: true}, Age: 1} db.Debug().Create(&u1) //INSERT INTO `users` (`name`,`age`) VALUES ('',1) }

CRUD
创建
使用 db.NewRecord()查询主键是否存在,主键为空使用 db.Create()创建记录
result := db.Create(&user) fmt.Println("ID:", user.ID)//插入主键ID fmt.Println("ERROR:", result.Error)//错误 fmt.Println("ROWS:", result.RowsAffected) //影响行数

查询 一般查询
var user User //获取第一条记录(主键升序) db.First(&user) // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// 随机获取一条记录 db.Take(&user) //SELECT * FROM users LIMIT 1; // 获取最后一条记录(主键降序) db.Last(&user) // SELECT * FROM users ORDER BY id DESC LIMIT 1; //获取全部记录 db.Find(&user) //SELECT * FROM `users`

Where条件查询
//条件查询单条记录 db.Where("name=?", "AAA").First(&user) //SELECT * FROM `users` WHERE name='AAA' ORDER BY `users`.`id` LIMIT 1//条件查询多条记录 db.Where("name=?", "AAA").Find(&user) //SELECT * FROM `users` WHERE name='AAA'//不等于 db.Where("name <> ?", "AAA").Find(&user) //SELECT * FROM `users` WHERE name<>'AAA'//IN db.Where("name IN (?)", []string{"AAA", "BBB"}).Find(&user) //SELECT * FROM `users` WHERE name IN ('AAA','BBB')//AND db.Debug().Where("name=? AND age=?", "AAA", 11).Find(&user) //SELECT * FROM `users` WHERE name='AAA' AND age=11

Struct & Map条件查询
//Struct db.Where(&User{Name: "AAA"}).Find(&user) //SELECT * FROM `users` WHERE `users`.`name` = 'AAA' //*当使用结构作为条件查询时,GORM 只会查询非零值字段。//Map db.Where(map[string]interface{}{"name": "AAA"}).Find(&user) //SELECT * FROM `users` WHERE `name` = 'AAA' //如果想要包含零值查询条件,你可以使用 map,其会包含所有 key-value 的查询条件//主键切片 db.Where([]int64{1, 2, 3}).Find(&user) //SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3)

内联条件 查询条件也可以被内联到 FirstFind 之类的方法中,其用法类似于 Where
db.First(&user, "id = ?", 1)

Not条件
db.Not("ID=?", 1).First(&user) //SELECT * FROM `users` WHERE NOT ID=1 ORDER BY `users`.`id` LIMIT 1

Or条件
db.Where("ID=?", 1).Or("ID=?", 2).First(&user) //SELECT * FROM `users` WHERE ID=1 OR ID=2 ORDER BY `users`.`id` LIMIT 1

选择特定字段
db.Select("name", "age").First(&user) //SELECT `name`,`age` FROM `users` ORDER BY `users`.`id` LIMIT 1

排序
db.Order("age desc").Order("name asc").Find(&user) //SELECT * FROM `users` ORDER BY age desc,name asc

分组Group By & Having
db.Group("name").Having("name=?", "AAA").Find(&user)

Joins
db.Debug().Joins("left join emails on emails.uid = users.id").Find(&user) //SELECT * FROM `users` left join emails on emails.uid = users.id

Pluck 查询 model 中的一个列作为切片(多列用Scan)
var ages []int64 db.Debug().Model(&User{}).Pluck("age", &ages) //SELECT `age` FROM `users`db.Debug().Model(&User{}).Select("name", "age").Scan(&user) //SELECT `name`,`age` FROM `users`

立即执行方法
Create`, `First`, `Find`, `Take`, `Save`, `UpdateXXX`, `Delete`, `Scan`, `Row`, `Rows

使用多个立即执行方法时,后一个立即执行方法会复用前一个立即执行方法的条件 (不包括内联条件) 。
db.Find(&user).Count(&count)//SELECT * FROM `users` //SELECT count(*) FROM `users`

注意 Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT
更新 更新所有字段 Save()默认会更新该对象的所有字段,即使你没有赋值。
var user User user.Name = "SSS" db.Debug().Save(&user) //INSERT INTO `users` (`name`,`age`) VALUES ('SSS',0)

更新指定字段 更新单个列:Update
当使用 Update 更新单个列时,你需要指定条件,否则会返回 ErrMissingWhereClause 错误。
// 更新单个属性,如果它有变化 db.Model(&user).Update("name", "hello") //// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; // 根据给定的条件更新单个属性 db.Model(&user).Where("active = ?", true).Update("name", "hello") //// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多个列:Updates
Updates 方法支持 structmap[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段
// 使用 map 更新多个属性,只会更新其中有变化的属性 db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) //// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111; // 使用 struct 更新多个属性,只会更新其中有变化且为非零值的字段 db.Model(&user).Updates(User{Name: "hello", Age: 18}) //// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111; // 警告:当使用 struct 更新时,GORM只会更新那些非零值的字段 // 对于下面的操作,不会发生任何更新,"", 0, false 都是其类型的零值 db.Model(&user).Updates(User{Name: "", Age: 0, Active: false})

更新选定字段 更新时选定字段:Select
【GO笔记04 - GORM】更新时忽略字段:Omit
// 使用 Map 进行 Select // User's ID is `111`: db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET name='hello' WHERE id=111; db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) // UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111; // 使用 Struct 进行 Select(会 select 零值的字段) db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0}) // UPDATE users SET name='new_name', age=0 WHERE id=111; // Select 所有字段(查询包括零值字段的所有字段) db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})// Select 除 Role 外的所有字段(包括零值字段的所有字段) db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

使用SQL表达式更新
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))

删除
// Email 的 ID 是 `10` db.Delete(&email) // DELETE from emails where id = 10; // 带额外条件的删除 db.Where("name = ?", "jinzhu").Delete(&email) // DELETE from emails where id = 10 AND name = "jinzhu";

软删除
// user's ID is `111` db.Delete(&user) // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111; // Batch Delete db.Where("age = ?", 20).Delete(&User{}) // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20; // Soft deleted records will be ignored when querying db.Where("age = 20").Find(&user) // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL; db.Unscoped().Where("age = 20").Find(&users) // SELECT * FROM users WHERE age = 20; db.Unscoped().Delete(&order) // DELETE FROM orders WHERE id=10;

参考资料:
GORM中文站:https://gorm.io/zh_CN/
GORM入门指南:https://www.liwenzhou.com/pos...
GORM CRUD:https://www.liwenzhou.com/pos...

    推荐阅读