GO_gorm
gorm
-
概念
-
- 将GO中的结构体与数据库中的表相对应:
数据表 --- 结构体
数据行---结构体示例
字段---结构体字段
-
-
优缺点
- 提升开发效率
- 牺牲执行效率
- 牺牲灵活性
- 弱化SQL能力
-
-
环境准备
-
-
安装
-
go get -u gorm.io/gorm
//根据所用数据库执行相应的指令 go get -u gorm.io/driver/sqlite go get -u gorm.io/driver/mysql
-
-
连接到数据库
-
func main() {
// 使用 GORM 连接到 MySQL 数据库
db, err := gorm.Open("mysql", "usr1:12345@tcp(*******:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err.Error())
}
fmt.Println("Connected to the database successfully!")
}
-
-
-
-
导入
-
//gorm导入 go get gorm.io/gorm //终端下载mysql的驱动: go get gorm.io/driver/mysql
-
-
-
基础
-
模型
-
type User struct { ID uint // Standard field for the primary key Name string // A regular string field Email *string // A pointer to a string, allowing for null values Age uint8 // An unsigned 8-bit integer Birthday *time.Time // A pointer to time.Time, can be null MemberNumber sql.NullString // Uses sql.NullString to handle nullable strings ActivatedAt sql.NullTime // Uses sql.NullTime for nullable time fields CreatedAt time.Time // Automatically managed by GORM for creation time UpdatedAt time.Time // Automatically managed by GORM for update time ignored string // fields that aren't exported are ignored }
- 具体数字类型如
uint、string和uint8直接使用。 - 指向
*string和*time.Time类型的指针表示可空字段。 - 来自
database/sql包的sql.NullString和sql.NullTime用于具有更多控制的可空字段。 CreatedAt和UpdatedAt是特殊字段,当记录被创建或更新时,GORM 会自动向内填充当前时间。
-
-
约定
-
主键:
- GORM 使用一个名为
ID的字段作为每个模型的默认主键 - 也可以使用使用gorm的tag设置主键。
-
// 使用`AnimalID`作为主键 type Animal struct { AnimalID int64 `gorm:"primary_key"` Name string Age int64 }
- GORM 使用一个名为
-
表名:
- 表名默认就是结构体名称的复数。
- 禁用默认表名的复数形式,如果置为 true,则
User的默认表名是user,如下: -
func main() {//禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user` db.SingularTable(true)// db.AutoMigrate(&User{}) fmt.Println("Connected to the database successfully!") }
-
列名:
- 默认:列名由字段名称进行下划线分割来生成:两个单词以上组成的字段会使用下划线分割。
- 时间戳字段:
-
CreatedAt:字段的值将会是初次创建记录的时间。
-
db.Create(&user) // `CreatedAt`将会是当前时间 // 可以使用`Update`方法来改变`CreateAt`的值 db.Model(&user).Update("CreatedAt", time.Now())
-
-
UpdatedAt
-
db.Save(&user) // `UpdatedAt`将会是当前时间 db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间
-
-
DeletedAt
-
db.Save(&user) // `UpdatedAt`将会是当前时间 db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间
-
-
-
-
预定义结构体
-
GORM提供了一个预定义的结构体,名为gorm.Model,其中包含常用字段:
-
-
// gorm.Model 的定义 typeModel struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` }
-
高级选项
-
嵌入结构体
-
- 对于匿名字段,GORM 会将其字段包含在父结构体中,例如:
-
-
-
-
type Author struct { Name string Email string } type Blog struct { Author ID int Upvotes int32 } // equals type Blog struct { ID int64 Name string Email string Upvotes int32 }
-
对于正常的结构体字段,你也可以通过标签
embedded将其嵌入,例如: -
type Author struct { Name string Email string } type Blog struct { ID int Author Author `gorm:"embedded"` Upvotes int32 } // 等效于 type Blog struct { ID int64 Name string Email string Upvotes int32 }
-
并且,您可以使用标签
embeddedPrefix来为 db 中的字段名添加前缀,例如: -
type Blog struct { ID int Author Author `gorm:"embedded;embeddedPrefix:author_"` Upvotes int32 } // 等效于 type Blog struct { ID int64 AuthorName string AuthorEmail string Upvotes int32 }
-
-
-
-
字段标签
-
-
-
-
标签名 说明 column 指定 db 列名 type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如: not null、size,autoIncrement… 像varbinary(8)这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENTserializer 指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtimesize 定义列数据类型的大小或长度,例如 size: 256primaryKey 将列定义为主键 unique 将列定义为唯一键 default 定义列的默认值 precision 指定列的精度 scale 指定列大小 not null 指定列为 NOT NULL autoIncrement 指定列为自动增长 autoIncrementIncrement 自动步长,控制连续记录之间的间隔 embedded 嵌套字段 embeddedPrefix 嵌入字段的列名前缀 autoCreateTime 创建时追踪当前时间,对于 int字段,它会追踪时间戳秒数,您可以使用nano/milli来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nanoautoUpdateTime 创建/更新时追踪当前时间,对于 int字段,它会追踪时间戳秒数,您可以使用nano/milli来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milliindex 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 uniqueIndex 与 index相同,但创建的是唯一索引check 创建检查约束,例如 check:age > 13,查看 约束 获取详情<- 设置字段写入的权限, <-:create只创建、<-:update只更新、<-:false无写入权限、<-创建和更新权限-> 设置字段读的权限, ->:false无读权限- 忽略该字段, -表示无读写,-:migration表示无迁移权限,-:all表示无读写迁移权限comment 迁移时为字段添加注释
-
-
-
CURD
-
create
-
Create方法:创建新记录并将其保存到数据库中-
func main() {
//连接数据库...user := User{Name: "John Doe", Email: "john@example.com"}
db.Create(&user)}
-
-
-
-
-
BatchInsert方法:批量插入多个记录-
users := []User{ {Name: "John Doe", Email: "john@example.com"}, {Name: "Jane Smith", Email: "jane@example.com"}, } db.Create(&users)
-
-
FirstOrCreate方法:查找符合条件的记录,如果不存在则创建新记录
-
-
-
-
-
user := User{Name: "John Doe", Email: "john@example.com"}
db.FirstOrCreate(&user, User{Name: "John Doe"})
-
-
Save方法:保存记录到数据库,如果记录不存在则创建新记录-
user := User{Name: "John Doe", Email: "john@example.com"}
db.Save(&user)
-
-
-
-
-
-
Omit方法:忽略指定字段,在保存记录时不更新这些字段-
db.Omit("Email").Create(&user)
-
-
-
-
-
-
Assign方法:为记录的字段赋值,但不保存到数据库-
db.Model(&user).Assign(User{Email: "newemail@example.com"}).FirstOrCreate(&user)
-
-
-
-
-
Read(读取/查询)
-
一般查询
-
Find 方法:查询所有符合条件的记录。
var users []User
db.Find(&users)
First 方法:查询符合条件的第一条记录。
var user User
db.First(&user)
Last 方法:查询符合条件的最后一条记录。
var user User
db.Last(&user)
Take 方法:随机获取一条记录。
var user User
db.Take(&user)
Find 方法(带条件):根据条件查询符合条件的记录。
var users []User
db.Where("age > ?", 8).Find(&users)
Find 方法(多个条件):根据多个条件查询符合条件的记录。
var users []User
db.Where("age > ? AND name LIKE ?", 8, "%Doe%").Find(&users)
First 方法(带条件):根据条件查询符合条件的第一条记录。
var user User
db.Where("name = ?", "John Doe").First(&user)
Last 方法(带条件):根据条件查询符合条件的最后一条记录。
var user User
db.Where("name = ?", "John Doe").Last(&user)
Take 方法(带条件):根据条件随机获取一条记录。
var user User
db.Where("age > ?", 8).Take(&user)
Find 方法(分页查询):根据分页条件查询记录。
var users []User
db.Offset(0).Limit(5).Find(&users)
Select 方法:选择特定的字段进行查询。
var names []string
db.Model(&User{}).Select("name").Find(&names)
Pluck 方法:从查询结果中提取特定字段值。
var names []string
db.Model(&User{}).Pluck("name", &names)
-
-
-
-
高级查询
-
-
-
Where 方法:设置查询条件。 var users []User db.Where("age > ?", 8).Where("name LIKE ?", "%Doe%").Find(&users) Or 方法:设置 OR 条件。 var users []User db.Where("age > ?", 8).Or("name LIKE ?", "%Doe%").Find(&users) Not 方法:设置 NOT 条件。 var users []User db.Not("age", 5).Find(&users) Order 方法:设置结果排序方式。 var users []User db.Order("age desc").Find(&users) Limit 方法:设置结果返回数量的限制。 var users []User db.Limit(0).Find(&users) Offset 方法:设置结果的偏移量。 var users []User db.Offset(5).Find(&users) Select 方法:选择特定的字段进行查询。 var names []string db.Model(&User{}).Select("name").Find(&names) Joins 方法:执行联接查询。 var users []User db.Joins("JOIN profiles ON users.id = profiles.user_id").Find(&users) Group 方法:按指定字段进行分组。 var results []map[string]interface{} db.Model(&User{}).Select("age, count(*) as count").Group("age").Scan(&results) Having 方法:设置分组后的条件。 var results []map[string]interface{} db.Model(&User{}).Select("age, count(*) as count").Group("age").Having("count > ?", ).Scan(&results)
-
-
Struct & Map查询
-
// Struct db.Where(&User{Name: "李四", Age: 20}).First(&user) //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1; // Map db.Where(map[string]interface{}{"name": "李四", "age": 20}).Find(&user) //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20; // 主键的切片 db.Where([]int64{1, 2}).Find(&users) //// SELECT * FROM users WHERE id IN (1, 2);
-
-
-
update
-
-
-
Save 方法:保存记录到数据库,包括更新已存在的记录。
user := User{Name: "John Doe", Email: "john@example.com"}
db.Save(&user)Update 方法:更新指定的字段值。
db.Model(&user).Update("Email", "newemail@example.com")Updates 方法:更新指定字段的值,并将更改保存到数据库。
db.Model(&user).Updates(User{Email: "newemail@example.com"})UpdateColumn 方法:更新指定字段的值,但不更新其他字段。
db.Model(&user).UpdateColumn("Email", "newemail@example.com")UpdateColumns 方法:更新指定字段的值,但不更新其他字段。
db.Model(&user).UpdateColumns(map[string]interface{}{"Email": "newemail@example.com", "Name": "John Doe"})Select 方法:更新指定字段的值。
db.Model(&user).Select("Email").Updates(User{Email: "newemail@example.com"})Omit 方法:忽略指定字段,在更新记录时不更新这些字段。
db.Omit("Email").Save(&user)Assign 方法:为记录的字段赋值,但不保存到数据库。
db.Model(&user).Assign(User{Email: "newemail@example.com"}).Save(&user)
-
-
-
-
delete
-
-
-
-
Delete 方法:删除指定的记录。
db.Delete(&user)
Delete 方法(带条件):根据条件删除符合条件的记录。
db.Where("name = ?", "John").Delete(&User{})
Unscoped 方法:永久删除记录,包括软删除的记录。
db.Unscoped().Delete(&user)
Delete 方法(批量删除):批量删除符合条件的记录。
db.Where("age < ?", 18).Delete(&User{})
Delete 方法(根据主键):根据主键删除记录。
db.Delete(&User{}, 1)
Truncate 方法:清空表中的所有记录。
db.Exec("TRUNCATE TABLE users")
-
-
-
关联
FOREIGN KEY: 一个表中的 FOREIGN KEY 指向另一个表中的 UNIQUE KEY(唯一约束的键)。
REFERENCES: 确定外键指向的另外一个表的字段
-
多对一
-
默认情况
-
// `User` 属于 `Company`,`CompanyID` 是外键 type User struct { gorm.Model Name string CompanyID int // 默认外键 Company Company } type Company struct { ID int Name string } ompanyID 被隐含地用来在 User 和 Company 之间创建一个外键关系, 因此必须包含在 User 结构体中才能填充 Company 内部结构体
-
重写外键
-
type UserB struct { gorm.Model Name string CompanyIDS int Company Company `gorm:"foreignKey:CompanyIDS"` } type Company struct { ID int CardNumber int `gorm:"unique"` Name string }
指定CompanyIDS 作为外键, 和Company 中的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 } User 中CompanyID 作为外键,指定与 comapny中的code相对应
-
-
-
一对一
-
-
-
// User 有一张 CreditCard,UserID 是外键 type User struct { gorm.Model CreditCard CreditCard } type CreditCard struct { gorm.Model Number string UserID uint }
亦可以使用foreignKey和references -
一对多
-
// User 有多张 CreditCard,UserID 是外键 type User struct { gorm.Model CreditCards []CreditCard } type CreditCard struct { gorm.Model Number string UserID uint }
-
-
多对多
-
// User 拥有并属于多种 language,`user_languages` 是连接表 type User struct { gorm.Model Languages []Language `gorm:"many2many:user_languages;"` } type Language struct { gorm.Model Name string }
-
-
-
事务
实际上gorm是每执行一个命令函数就向数据库发送一条SQL,事务的功能是通过数据库保证的,gorm库并没有对应的逻辑实现事务。
当我们执行db.Begin()时,gorm库会帮我们发送“Start Transaction”这条SQL语句到数据库中执行,在我们执行相应的Select、Update命令时也会生成对应的SQL语句并发送到数据库中执行。最后,当我们执行tx.Commit()时,gorm库会发送“COMMIT”到数据库中执行,从而完成整个数据库的运行。
-
-
手动事务(Manual Transactions)
-
手动事务需要显式调用 Begin 开启事务,通过 Commit 提交事务,或 Rollback 回滚事务,全程由开发者控制事务生命周期,适合复杂业务逻辑(如多步操作需手动判断是否提交)。
-
-
-
基本流程
-
-
开启事务:调用 db.Begin() 获取事务对象 tx *gorm.DB。
执行操作:通过事务对象 tx 执行数据库操作(如 Create、Update、Delete)。
判断结果:若所有操作成功,调用 tx.Commit() 提交;若失败,调用 tx.Rollback() 回滚。
-
-
-
示例
-
// 定义模型 type User struct { gorm.Model Name string Balance int // 余额 } // 手动事务实现转账 func transfer(db *gorm.DB, fromID, toID, amount int) error { // 1. 开启事务 tx := db.Begin() if tx.Error != nil { return tx.Error // 开启事务失败(如数据库连接问题) } // 2. 执行事务内操作 var fromUser, toUser User // 查A的账户 if err := tx.First(&fromUser, fromID).Error; err != nil { tx.Rollback() // 查询失败,回滚 return err } // 查B的账户 if err := tx.First(&toUser, toID).Error; err != nil { tx.Rollback() return err } // A的余额不足,回滚 if fromUser.Balance < amount { tx.Rollback() return fmt.Errorf("余额不足") } // A扣钱 if err := tx.Model(&fromUser).Update("balance", fromUser.Balance - amount).Error; err != nil { tx.Rollback() return err } // B加钱 if err := tx.Model(&toUser).Update("balance", toUser.Balance + amount).Error; err != nil { tx.Rollback() return err } // 3. 所有操作成功,提交事务 return tx.Commit().Error }
-
-
自动事务(Auto Transactions)
自动事务通过
db.Transaction方法封装事务逻辑,传入一个函数作为事务内的操作,Gorm 会自动开启事务、执行函数、根据函数返回值决定提交或回滚,简化代码。-
// 自动事务实现转账 func transferAuto(db *gorm.DB, fromID, toID, amount int) error { // 调用 db.Transaction,传入事务内操作的函数 return db.Transaction(func(tx *gorm.DB) error { var fromUser, toUser User // 查A的账户 if err := tx.First(&fromUser, fromID).Error; err != nil { return err // 返回错误,Gorm 自动回滚 } // 查B的账户 if err := tx.First(&toUser, toID).Error; err != nil { return err } // 余额不足,返回错误 if fromUser.Balance < amount { return fmt.Errorf("余额不足") } // A扣钱 if err := tx.Model(&fromUser).Update("balance", fromUser.Balance - amount).Error; err != nil { return err } // B加钱 if err := tx.Model(&toUser).Update("balance", toUser.Balance + amount).Error; err != nil { return err } return nil // 无错误,Gorm 自动提交 }) }
-
-
嵌套事务(Nested Transactions)
嵌套事务指在一个事务内部开启另一个事务,外层事务称为“父事务”,内层称为“子事务”。Gorm 通过数据库的
SAVEPOINT(保存点)实现嵌套事务,支持内层事务独立回滚而不影响外层。-
// 嵌套事务示例 func nestedTransaction(db *gorm.DB) error { return db.Transaction(func(parentTx *gorm.DB) error { // 父事务:创建用户A userA := User{Name: "Alice", Balance: 1000} if err := parentTx.Create(&userA).Error; err != nil { return err // 父事务回滚 } // 开启子事务(嵌套事务) if err := parentTx.Transaction(func(childTx *gorm.DB) error { // 子事务:创建用户B userB := User{Name: "Bob", Balance: 500} if err := childTx.Create(&userB).Error; err != nil { return err // 子事务回滚(仅回滚自身操作,不影响父事务的userA) } // 子事务内主动回滚(例如业务判断失败) return fmt.Errorf("子事务主动回滚") // 仅子事务回滚,userB 不会被创建 }); err != nil { fmt.Printf("子事务失败:%v,但父事务可继续\n", err) } // 父事务继续执行:更新用户A的余额 return parentTx.Model(&userA).Update("balance", 2000).Error }) }
-
-

浙公网安备 33010602011771号