gorm入门学习
2022/2/27 6:21:28
本文主要是介绍gorm入门学习,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文档
https://learnku.com/docs/gorm/v2/v2_release_note/9756 https://gorm.cn/zh_CN/docs/
下载安装
go get -u gorm.io/gorm go get -u gorm.io/driver/mysql
简单示例
package main import ( "fmt" "time" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) // 模型定义 type Product struct { ID uint Code string Price uint CreateAt time.Time UpdateAt time.Time } func main(){ // gorm数据库提前创建好 dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local" // 创建连接 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil{ fmt.Printf("conncetion failed, err:%v", err) } // 迁移创建表,默认创建到数据库的表名为结构体名+s db.AutoMigrate(&Product{}) // 创建一条数据 product := Product{ ID: 1, Code: "10086", Price: 120, CreateAt: time.Now(), UpdateAt: time.Now(), } db.Create(&product) // 读 var product Product db.First(&product, 1) // 根据整形主键查找 db.First(&product, "code = ?", "D42") // 查找code字段值为D42 // 更新单条 db.Model(&product).Update("Price", 200) //将表products的price字段的值更新为200 // 更新多个字段 db.Model(&product).Updates(Product{Price:200, Code:"F42"})// 仅更新非零值字段 // 使用map类型更新 db.Model(&product).Updates(map[string]interfaces{}{ "Price": 200, "Code": "F42" }) // 删除 db.Delete(&product, 1) // 根据主键删除 }
声明模型
// 模型定义 type User struct{ ID uint Name string Email *string Age uint8 Birthday *time.Time MemberNumber sql.NullString ActivedAt sql.NullTime CreateAt time.Time UpdateAt time.Time } // gorm内部自己的结构体gorm.Model,可以直接嵌套到自己的结构体中 type Model struct{ ID uint `gorm:"primaryKey"` CreateAt time.Time UpdateAt time.Time DeleteAt gorm.DeleteAt `gorm:"index"` } // 创建/更新时间 type User struct { CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充 UpdatedAt int // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充 } // 正常结构体字段通过标签嵌入 type Author struct{ Name string Email string } type Blog struct{ ID int Author Author `gorm:"embedded" Upvotes int32` } // 通过标签来为db中的字段名添加前缀 type Blog struct{ ID int Author Author `gorm:"embedded;embeddedPrefix:author_"` Upvotes int32 } // 等效于 type Blog struct { ID int Author Author `gorm:"embedded;embeddedPrefix:author_"` Upvotes int32 } // 等效于 type Blog struct { ID int64 AuthorName string AuthorEmail string Upvotes int32 }
修改表名
package main import ( "gorm.io/driver/mysql" "gorm.io/gorm" ) type Student struct{ // 对应到 students这个表名 Id int // id Name String // name // Score float32 // score // tag方式 Rank float32 `gorm:"column:score"` //score // UpdateAt time.Time // update_at } // 修改表名 默认表名是结构体名小写+s func (Student) TableName() string{ return "student" }
字段标签
标签名 | 说明 |
---|---|
column | 指定 db 列名 |
type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null 、size , autoIncrement … 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSINED not NULL AUTO_INSTREMENT |
size | 指定列大小,例如:size:256 |
primaryKey | 指定列为主键 |
unique | 指定列为唯一 |
default | 指定列的默认值 |
precision | 指定列的精度 |
scale | 指定列大小 |
not null | 指定列为 NOT NULL |
autoIncrement | 指定列为自动增长 |
embedded | 嵌套字段 |
embeddedPrefix | 嵌入字段的列名前缀 |
autoCreateTime | 创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano /milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano |
autoUpdateTime | 创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano /milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli |
index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 |
uniqueIndex | 与 index 相同,但创建的是唯一索引 |
check | 创建检查约束,例如 check:age > 13 ,查看 约束 获取详情 |
<- | 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限 |
-> | 设置字段读的权限,->:false 无读权限 |
- | 忽略该字段,- 无读写权限 |
mysql驱动程序的一些高级配置
db, err := gorm.Open(mysql.New(mysql.Config{ DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name DefaultStringSize: 256, // string 类型字段的默认长度 DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置 }), &grom.Config{})
gorm 增删改查
gorm 增
// 结构体定义 type User struct{ ID uint Name string Email *string Age uint8 Birthday *time.Time MemberNumber sql.NullString ActivedAt sql.NullTime CreateAt time.Time UpdateAt time.Time } // helper function func timePtr(t time.Time) *time.Time{ return &t } // 创建一条数据 // Birthday为指针类型,需要创建一个helper函数来返回time.Time的地址,不能直接使用&来直接取地址 user := User{Name: "jinzhu", Age: 18, Birthday: timePtr(time.Now())} result := db.Create(&user) fmt.Println(user.ID) // 返回数据的主键 fmt.Println(result.RowsAffected) // 插入记录的条数 // 选定字段创建 user := User{Name: "liuwei", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()} db.Select("Name", "Age", "CreateAt").Create(&user) // Omit排除选定字段 user := User{Name: "jack", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()} db.Omit("Age", "CreateAt").Create(&user) // 钩子,创建记录时会自动调用这些方法 // BeforeSave/BeforeCreate, AfterSave/AfterCreate func (u *User) BeforeCreate(tx *gorm.DB)(err error){ fmt.Println("创建之前调用...") return nil } user := User{Name: "JOJO", Age: 24, Birthday: timePtr(time.Now()), CreatedAt: time.Now()} db.Create(&user) // 批量插入 var users = []User{{Name: "qiqi1"}, {Name: "qiqi2"}, {Name: "qiqi3"}} db.Create(&users) for _, user := range users { fmt.Println(user.ID) } // 使用map创建 db.Model(&User{}).Create(map[string]interface{}{ "Name":"wanglei", "Age": 25, }) // 批量map插入 db.Model(&User{}).Create([]map[string]interface{}{ {"Name":"xiao1", "Age": 22}, {"Name":"xiao2", "Age": 23}, {"Name":"xiao3", "Age": 24}, }) // 关联创建(一对一),外键会自动关联 db.Create(&User{ Name: "liuwei", CreditCard: CreditCard{Number: "12345678"} }) // 跳过关联创建数据 user := User{Name:"wanglei", CreditCard: CreditCard{Number: "11111111"}} db.Omit("CreditCard").Create(&user) // 跳过所有关联clause.Associations db.Omit(clause.Associations).Create(&user) // uuid go get -u github.com/satori/go.uuid // 简单使用 u, _ := uuid.NewV4() //uuid.UUID类型,NewV4()每次返回不同uuid // 默认值使用标签default func (u *User) BeforeCreate(tx *gorm.DB) (err error) { u.UUID = uuid.NewV4().String() return nil } type User struct { ID int64 Name string `gorm:"default: galeone"` Age int64 `gorm:"default:18"` UUID string } func main(){ // 默认会自动创建uuid db.Create(&User{Name: "liuwei1", Age: 28}) db.Create(&User{Name: "liuwei2", Age: 29}) } // 默认值零值问题 // 当Age传入0时,存入到数据时会被识别为default,即使用default的值18 db.Create(&User{Name: "liuwei3", Age: 0}) // 零值问题解决 // 将Age的类型int64改为指针类型*int64 type User struct { ID int64 Name string `gorm:"default: galeone"` Age *int64 `gorm:"default:18"` UUID string }
gorm 查
var user User //获取第一条记录(默认按主键升序) db.First(&user) fmt.Println(user.ID) fmt.Println(user.Name) // 获取第一条记录,没有指定排序字段 db.Take(&user) fmt.Println(user.ID) fmt.Println(user.Name) // 获取最后一条记录 db.Last(&user) //如果struct没有显示的定义主键,则按第一个字段排序 type Language struct { Code string Name string } db.First(&Language{}) // 主键检索 // 只支持整形,字符串会导致sql注入问题 // 查询id=10的数据 db.First(&user, 10) // 查询id在1,2,3中的 db.Find(&user, []int{1,2,3}) // 检索对象 // 获取全部数据,传入对象 db.Find(&user) //获取第一条匹配的记录 db.Where("name = ?", "jinzhu").First(&user) // 获取全部匹配记录 db.Where("name <> ?", "jinzhu").Find(&user) // IN db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&user) // LIKE db.Where("name LIKE ?", "%jin%").Find(&user) //AND db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&user) // Time db.Where("updated_at > ?", lastWeek).Find(&user) // BETWEEN db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&user) // struct类型查询 // 当使用结构作为条件查询时,GORM 只会查询非零值字段 db.Where(&User{Name: "jinzhu", Age: 20}).First(&user) // Age不会作为查询条件 db.Where(&User{Name: "jinzhu", Age: 0}).First(&user) // Map类型查询,可以使用零值作为查询条件 db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&user) // 主键切片 db.Where([]int64{20,21,22}).Find(&user) // 内联条件,用法与where相同 db.Find(&user, "name = ?", "jinzhu") // Not条件 db.Not("name = ?", "jinzhu").First(&user) // Or条件 db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&user) // 查询指定字段 db.Select("name", "age").Find(&user) // COALESCE()函数返回传入的参数中第一个非null的值 db.Table("users").Select("COALESCE(age,?)", 42).Rows() // SELECT COALESCE(age,'42') FROM users; // Order排序 db.Order("age desc, name").Find(&user) // 指定多个字段排序 db.Order("id desc").Order("age asc").Find(&users) //Limit限制返回的数量 db.Limit(3).Find(&user) // Offset跳过指定的数量返回 db.Offset(3).Find(&user) // Group分组 Having分组后过滤 type result struct{ Date time.Time Total int } db.Model(&User{}).Select("name,sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result) // SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name` db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result) // SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group" rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows() for rows.Next() { ... } rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows() for rows.Next() { ... } type Result struct { Date time.Time Total int64 } db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results) // Distinct 去重 db.Distinct("name", "age").Order("name, age desc").Find(&result) // Joins,左查询,右查询,内联查询 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{}) 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) // 带参数的多表连接 db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user) // Joins预加载,查询本表的同时将相关的外键表也同时查询出来 db.Joins("Company").Find(&users) // Scan结果至struct,用法于Find类似 type Result struct{ Name string Age int } var result Result db.Table("users").Select("name", "age").Where("name = ?", "auto").Scan(&result) // 原生sql Raw db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
gorm 更新
// Save保存 type User struct{ ID int64 Name string Age int64 Birthday time.Time UpdateAt time.Time } var user User db.First(&user) user.Name = "jinzhu666" user.Age = 100 db.Save(&user) // 更新单个列,必须指定条件,且该对象主键有值 // 当active=true时,更新name的值为hello db.Model(&User{}).Where("active = ?", true).Update("name", "hello") // 更新多列 // 根据struct更新,只会更新非零值的字段 db.Model(&user).Updates(User{Name: "hello", Age: 13, Active: false}) // 根据map更新属性 db.Model(&user).Updates(map[string]interfaces{}{ "name": "hello", "age": 18, "actived": false }) // 更新钩子 // 更新记录时会被自动调用 func (u *User) BeforeUpdate(tx *gorm.DB)(err error){ if u.Role == "admin"{ return errors.New("admin user not allowed to update") } return } // 批量更新Updates db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18}) // 使用sql表达式更新 // product 的 ID 是 `3` DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100)) // UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3; // 在更新时修改值 若要在 Before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 Save;否则,应该使用 scope.SetColumn ,例如: func (user *User) BeforeSave(scope *gorm.Scope) (err error) { if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil { scope.SetColumn("EncryptedPassword", pw) } } db.Model(&user).Update("Name", "jinzhu")
gorm 删除
// 删除一条记录,删除对象需要指定主键,否则会触发批量删除 var email Email email.Id = 10 db.Delete(&email) // 带条件删除 db.Where("name = ?", "jinzhu").Delete(&email) // 根据主键删除 db.Delete(&User{}, 10) var user User db.Delete(&user, []int{1,2,3}) // 删除钩子 func (u *User) BeforeDelete(tx *gorm.DB) (err error) { if u.Role == "admin" { return errors.New("admin user not allowed to delete") } return } // 批量删除,必须加条件 db.Where("email LIKE ?", "%jinzhu%").Delete(Email{}) db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
创建表,一对一/一对多/多对多关联表
// 一对一 type CreditCard struct { gorm.Model Number string UserID uint } type User struct { gorm.Model Name string CreditCard CreditCard } func main() { dsn := "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { fmt.Printf("connection failed, err:%v", err) } // 注意一下结构体的顺序 db.AutoMigrate(&User{}, &CreditCard{}) } // 一对多 User 有多张 CreditCard,UserID 是外键 type User struct { gorm.Model CreditCards []CreditCard // 切片 } type CreditCard struct { gorm.Model Number string UserID uint } // 对多对 // 会在两个 model 中添加一张连接表,使用AutoMigrate创建表时,会自动创建第三表 // User 拥有并属于多种 language,`user_languages` 是连接表 type User struct { gorm.Model Languages []Language `gorm:"many2many:user_languages;"` } type Language struct { gorm.Model Name string } // 多对多,自定义第三张连接表 type Person struct { ID int Name string Addresses []Address `gorm:"many2many:person_addresses;"` } type Address struct { ID uint Name string } type PersonAddress struct { PersonID int AddressID int CreatedAt time.Time DeletedAt gorm.DeletedAt } func (PersonAddress) BeforeCreate(db *gorm.DB) error { // ... } // 修改 Person 的 Addresses 字段的连接表为 PersonAddress // PersonAddress 必须定义好所需的外键,否则会报错 err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
重写外键名
//默认的外键使用拥有者的类型名加上主字段名 //如下:其外键一般是 CompanyID type User struct { gorm.Model Name string CompanyRefer int Company Company `gorm:"foreignKey:CompanyRefer"` // 使用 CompanyRefer 作为外键 } type Company struct { ID int Name string }
更改外键
// 默认外键都是使用主键id作为外键 type User struct { gorm.Model Name string CompanyID string Company Company `gorm:"references:Code"` // 使用 Code 作为外键 } type Company struct { ID int Code string Name string }
外键约束
// 你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建 type User struct { gorm.Model Name string CompanyID int Company Company `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` } type Company struct { ID int Name string }
反向引用(多对多)
// User 拥有并属于多种 language,`user_languages` 是连接表 type User struct { gorm.Model Languages []*Language `gorm:"many2many:user_languages;"` } type Language struct { gorm.Model Name string Users []*User `gorm:"many2many:user_languages;"` }
关联标签
标签 | 描述 |
---|---|
foreignKey | 指定外键 |
references | 指定引用 |
polymorphic | 指定多态类型 |
polymorphicValue | 指定多态值、默认表名 |
many2many | 指定连接表表名 |
joinForeignKey | 指定连接表的外键 |
joinReferences | 指定连接表的引用外键 |
constraint | 关系约束,例如:OnUpdate 、OnDelete |
预加载
// 查找一张表的时候,将另外一张表的数据也同时查询出来,用于优化查询 type User struct { gorm.Model Username string Orders []Order } type Order struct { gorm.Model UserID uint Price float64 } // 查找 user 时预加载相关 Order db.Preload("Orders").Find(&users) db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users) // Joins预加载,多用于一对一关系, inner join加载 db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1) // 预加载全部关联clause.Associations type User struct { gorm.Model Name string CompanyID uint Company Company Role Role } db.Preload(clause.Associations).Find(&users)
这篇关于gorm入门学习的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用
- 2024-10-24AI ?先驱齐聚 BAAI 2024,发布大规模语言、多模态、具身、生物计算以及 FlagOpen 2.0 等 AI 模型创新成果。
- 2024-10-20goland工具下,如修改一个项目的标准库SDK的版本-icode9专业技术文章分享
- 2024-10-17Go学习:初学者的简单教程
- 2024-10-17Go学习:新手入门完全指南