4 GROM 教程 (golang)
4 GROM 教程
- 链式操作
- 链式操作
- 立即执行方法
- 范围
- 多个立即执行方法
- 线程安全
- 错误处理
- 错误处理
- 错误
- 记录未找到错误
- 钩子
- 对象生命周期
- 钩子函数
- 创建对象时
- 更新对象时
- 删除对象时
- 查询对象时
- 事务
- 事务
- 手动控制的事务
- 一个具体的例子
- 迁移
- 自动迁移
- 其它迁移工具
- 模型方法
- Has Table
- Create Table
- Drop table
- ModifyColumn
- DropColumn
- Add Indexes
- Remove Index
- Add Foreign Key
- Remove ForeignKey
- SQL 生成器
- 执行原生SQL
- `sql.Row` 和 `sql.Rows`
- 将 `sql.Rows` 扫描至 model
- 通过数据库接口 sql.DB
- 连接池
链式操作
链式操作
Method Chaining,Gorm 实现了链式操作接口,所以你可以把代码写成这样:
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")// 创建一个查询
tx := db.Where("name = ?", "jinzhu")// 添加更多条件
if someCondition {tx = tx.Where("age = ?", 20)
} else {tx = tx.Where("age = ?", 30)
}if yetAnotherCondition {tx = tx.Where("active = ?", 1)
}
在调用立即执行方法前不会生成 Query 语句,有时候这会很有用。
比如你可以抽取一个函数来处理一些通用逻辑。
立即执行方法
Immediate methods ,立即执行方法是指那些会立即生成 SQL 语句并发送到数据库的方法, 他们一般是 CRUD 方法,比如:
Create
, First
, Find
, Take
, Save
, UpdateXXX
, Delete
, Scan
, Row
, Rows
…
这有一个基于上面链式方法代码的立即执行方法的例子:
tx.Find(&user)
生成的 Sql
SELECT * FROM users where name = 'jinzhu' AND age = 30 AND active = 1;
范围
Scopes,Scope 是建立在链式操作的基础之上的。
基于它,你可以抽取一些通用逻辑,写出更多可重用的函数库。
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {return db.Where("amount > ?", 1000)
}func PaidWithCreditCard(db *gorm.DB) *gorm.DB {return db.Where("pay_mode_sign = ?", "C")
}func PaidWithCod(db *gorm.DB) *gorm.DB {return db.Where("pay_mode_sign = ?", "C")
}func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {return func (db *gorm.DB) *gorm.DB {return db.Scopes(AmountGreaterThan1000).Where("status IN (?)", status)}
}db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// 查找所有金额大于 1000 的信用卡订单db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// 查找所有金额大于 1000 的 COD 订单db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// 查找所有金额大于 1000 且已付款或者已发货的订单
多个立即执行方法
Multiple Immediate Methods,在 GORM 中使用多个立即执行方法时,后一个立即执行方法会复用前一个 立即执行方法的条件 (不包括内联条件) 。
db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
生成的 Sql
SELECT * FROM users WHERE name LIKE 'jinzhu%' AND id IN (1, 2, 3)SELECT count(*) FROM users WHERE name LIKE 'jinzhu%'
线程安全
所有链式方法都会创建并克隆一个新的 DB 对象 (共享一个连接池),GORM 在多 goroutine 中是并发安全的。
错误处理
Go的错误处理是很重要的
建议您在调用任何 立即执行方法 后进行错误检查
错误处理
GORM中的错误处理与惯用的Go代码不同,因为它具有可链接的API,但仍然易于实现。
如果发生任何错误,GORM将设置* gorm.DB的错误字段,可以这样检查:
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {// error 处理...
}
或者
if result := db.Where("name = ?", "jinzhu").First(&user); result.Error != nil {// error 处理...
}
错误
处理数据时,通常会发生多个错误。 GORM提供了一个API来将所有错误作为切片返回:
// 如果发生了一个以上的错误, `GetErrors` 以`[]error`形式返回他们
errors := db.First(&user).Limit(10).Find(&users).GetErrors()fmt.Println(len(errors))for _, err := range errors {fmt.Println(err)
}
记录未找到错误
RecordNotFound,GORM提供了处理 RecordNotFound
错误的快捷方式。如果有多个错误,它将逐一检查这些错误是否为 RecordNotFound
错误。
// 检查是否为 RecordNotFound 错误
db.Where("name = ?", "hello world").First(&user).RecordNotFound()if db.Model(&user).Related(&credit_card).RecordNotFound() {// 未找到记录
}if err := db.Where("name = ?", "jinzhu").First(&user).Error; gorm.IsRecordNotFoundError(err) {// 未找到记录
}
钩子
对象生命周期
Hooks(一般称之为钩子函数)的功能是在运行创建/查询/更新/删除语句之前或者之后执行。
如果你为一个 model 定义了一个具体的方法,它将会在运行 创建,更新,查询,删除时自动被调用,并且如果任何回调函数函数返回一个错误,GORM 将会停止接下来的操作并且回滚当前的事务。
钩子函数
创建对象时
Creating an object,创建对象时可用的 hooks
// 开始事务
BeforeSave
BeforeCreate
// 在关联前保存
// 更新时间戳 `CreatedAt`, `UpdatedAt`
// save self
// 重新加载具有默认值的字段,其值为空
// 在关联后保存
AfterCreate
AfterSave
// 提交或回滚事务
示例代码
func (u *User) BeforeSave() (err error) {if !u.IsValid() {err = errors.New("can't save invalid data")}return
}func (u *User) AfterCreate(scope *gorm.Scope) (err error) {if u.ID == 1 {scope.DB().Model(u).Update("role", "admin")}return
}
注意 在 GORM 中 Save/Delete 操作默认是基于事务完成, 所以相关更改在提交事务之前是不可见的。 如果你想在你的 hooks 中看到这些变化,你可以在你的 hooks 中接收当前事务的参数,比如:
func (u *User) AfterCreate(tx *gorm.DB) (err error) {tx.Model(u).Update("role", "admin")return
}
更新对象时
Updating an object,更新对象时可用的 hooks
// begin transaction 开始事物
BeforeSave
BeforeUpdate
// save before associations 保存前关联
// update timestamp `UpdatedAt` 更新 `UpdatedAt` 时间戳
// save self 保存自己
// save after associations 保存后关联
AfterUpdate
AfterSave
// commit or rollback transaction 提交或回滚事务
代码实例:
func (u *User) BeforeUpdate() (err error) {if u.readonly() {err = errors.New("read only user")}return
}// 在一个事务中更新数据
func (u *User) AfterUpdate(tx *gorm.DB) (err error) {if u.Confirmed {tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("verfied", true)}return
}
删除对象时
Deleting an object,删除对象时可用的 hooks
// begin transaction 开始事务
BeforeDelete
// delete self 删除自己
AfterDelete
// commit or rollback transaction 提交或回滚事务
代码实例:
// 在一个事务中更新数据
func (u *User) AfterDelete(tx *gorm.DB) (err error) {if u.Confirmed {tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false)}return
}
查询对象时
Querying an object,查询对象时可用的 hooks
// load data from database 从数据库加载数据
// Preloading (eager loading) 预加载(加载)
AfterFind
代码实例:
func (u *User) AfterFind() (err error) {if u.MemberShip == "" {u.MemberShip = "user"}return
}
事务
GORM 默认会将单个的 create
, update
, delete
操作封装在事务内进行处理,以确保数据的完整性。
如果你想把多个 create
, update
, delete
操作作为一个原子操作,Transaction
就是用来完成这个的。
事务
要在事务中执行一系列操作,通常您可以参照下面的流程来执行。
func CreateAnimals(db *gorm.DB) error {return db.Transaction(func(tx *gorm.DB) error {// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {// 返回任意 err ,整个事务都会 rollbackreturn err}if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {return err}// 返回 nil ,事务会 commitreturn nil})
}
手动控制的事务
// 开始事务
tx := db.Begin()// 在事务中做一些数据库操作 (这里应该使用 'tx' ,而不是 'db')
tx.Create(...)// ...// 有错误时,手动调用事务的 Rollback()
tx.Rollback()// 无错误时,手动调用事务的 Commit()
tx.Commit()
一个具体的例子
func CreateAnimals(db *gorm.DB) error {// 请注意,事务一旦开始,你就应该使用 tx 作为数据库句柄tx := db.Begin()defer func() {if r := recover(); r != nil {tx.Rollback()}}()if err := tx.Error; err != nil {return err}if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {tx.Rollback()return err}if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {tx.Rollback()return err}return tx.Commit().Error
}
迁移
自动迁移
自动迁移你的模型,使之保持最新状态。
警告: 自动迁移 只会 创建表、缺失的列、缺失的索引, 不会 更改现有列的类型或删除未使用的列,以此来保护您的数据。
db.AutoMigrate(&User{})db.AutoMigrate(&User{}, &Product{}, &Order{})// 创建表时添加表后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
其它迁移工具
GORM 的自动迁移在大多数情况下都会正常工作,但如果你需要更严格的迁移工具, GORM 提供了通用 DB interface ,这可能对你有帮助。
// 返回 `*sql.DB`
db.DB()
更多详细信息请参阅 通用接口。
模型方法
Has Table
// 检查模型 User 的表是否存在
db.HasTable(&User{})// 检查表 users 是否存在
db.HasTable("users")
Create Table
// 为模型 `User` 创建表
db.CreateTable(&User{})// 创建表时会追加 “ENGINE=InnoDB” 到 SQL 语句中。
db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{})
Drop table
// 删除模型 `User` 的表
db.DropTable(&User{})// 删除表 `users`
db.DropTable("users")// 删除模型 `User` 的表和表 `products`
db.DropTableIfExists(&User{}, "products")
ModifyColumn
修改列类型为给定的值
// 修改模型 `User` 的 description 列的类型为 `text`
db.Model(&User{}).ModifyColumn("description", "text")
DropColumn
// 删除模型 `User` 的 description 列
db.Model(&User{}).DropColumn("description")
Add Indexes
// 为 `name` 列添加名为 `idx_user_name` 的普通索引
db.Model(&User{}).AddIndex("idx_user_name", "name")// 为 `name` 和 `age` 两列添加名为 `idx_user_name_age` 的普通索引
db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age")// 添加唯一索引
db.Model(&User{}).AddUniqueIndex("idx_user_name", "name")// 为多列添加唯一索引
db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age")
Remove Index
// 删除索引
db.Model(&User{}).RemoveIndex("idx_user_name")
Add Foreign Key
// 添加外键
// 第一个参数: 外键字段
// 第二个参数:目标表名(字段)
// 第三个参数:删除时
// 第四个参数: 更新时
db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
Remove ForeignKey
db.Model(&User{}).RemoveForeignKey("city_id", "cities(id)")
SQL 生成器
执行原生SQL
执行原生 SQL 时,不支持与其它方法的链式操作
db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now(), []int64{11,22,33})// Scan
type Result struct {Name stringAge int
}var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
sql.Row
和 sql.Rows
通过 *sql.Row
或 *sql.Rows
获取查询结果
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {...rows.Scan(&name, &age, &email)...
}// 原生 SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {...rows.Scan(&name, &age, &email)...
}
将 sql.Rows
扫描至 model
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()for rows.Next() {var user User// ScanRows 扫描一行记录到 userdb.ScanRows(rows, &user)// do something
}
}
通过数据库接口 sql.DB
GORM 提供了 DB
方法 ,可以从当前 *gorm.DB
连接内,获取一个通用的数据库接口*sql.DB
// 获取通用 sql.DB 并使用其方法
db.DB()// Ping
db.DB().Ping()
注意 如果数据库底层连接的不是一个 *sql.DB
(比如在一个事务中),那么该方法会返回 nil
。
连接池
// SetMaxIdleCons 设置连接池中的最大闲置连接数。
db.DB().SetMaxIdleConns(10)// SetMaxOpenCons 设置数据库的最大连接数量。
db.DB().SetMaxOpenConns(100)// SetConnMaxLifetiment 设置连接的最大可复用时间。
db.DB().SetConnMaxLifetime(time.Hour)
4 GROM 教程 (golang)相关推荐
- 3 GROM 关联 (golang)
3 GROM 关联 属于 外键 关联外键 Belongs To 的使用 查询 查询 Where 条件 普通 SQL Struct & Map Not 条件 Or 条件 Inline Condi ...
- Kafka入门教程 Golang实现Kafka消息发送、接收
一:核心概念 kafka是消息中间件的一种,是一种分布式流平台,是用于构建实时数据管道和流应用程序.具有横向扩展,容错,wicked fast(变态快)等优点. kafka中涉及的名词: 消息记录(r ...
- JavaTPoint 编程语言中文教程【翻译完成】
原文:JavaTPoint 协议:CC BY-NC-SA 4.0 阶段:机翻(1) 危机只有发展到最困难的阶段,才有可能倒逼出有效的解决方案.--<两次全球大危机的比较研究> 在线阅读 在 ...
- LearnETutorial 中文系列教程【翻译完成】
原文:LearnETutorial 协议:CC BY-NC-SA 4.0 人最大的痛苦就是说一些自己都不相信的话.--燕京学堂鹿会 在线阅读 在线阅读(Gitee) ApacheCN 学习资源 目录 ...
- golang实现webgis后端开发
目录 前言 二.实现步骤 1.postgis数据库和model的绑定 2.将pg库中的要素转换为geojson (1)几何定义 (2)将wkb解析为几何类型 (3)定义geojson类型 (4)数据转 ...
- Go语言学习资料整理
整理网上找到的Golang语言学习资料 基础 基础教程 书籍在线版 Go 指南-A Tour of Go Go语言圣经(中文版) Effective Go中文版 Go Web编程 build-web- ...
- bool类型数组转换成一个整数_Go 学习笔记 02 | 基本数据类型以及 byte 和 rune 类型...
一.基本数据类型 unsafe.Sizeof() 查看不同长度的整型在内存中的存储空间. 类型转换,高位向低位转换要注意溢出. 数字字面量语法. 64 位系统中 Go 语言中浮点数默认是 float6 ...
- go语言和java比_Go VS Java:一位资深程序员对两种语言的解读
导读:对于软件开发的编程语言,其实没有万能灵药. 本文作者详细介绍了他使用Java和Go这两种编程语言,一个是传统语言,一个是新兴语言的工作方式. Go VS Java 实话说,我很喜欢Java这门语 ...
- Ethereum Architecture : 以太坊架构
Ethereum Architecture 去中心化背后的想法是不依赖单一/集中式服务器. 以太坊区块链到底是什么?区块链有两个主要组成部分: 数据库:网络中的每笔交易都存储在区块链中.当您部署应用程 ...
最新文章
- BZOJ3262 : 陌上花开
- ORA-04063: view SYS.DBA_REGISTRY has errors
- 【Linux】 Samba 服务器安装配置实现与Windows系统的文件共享服务
- mysql 录入窗体设计_在Access中,可用于设计输入界面的对象是 A)窗体 B)报表 C)查询 D)表...
- CTO俱乐部下午茶:技术团队管理中的那些事儿
- 信息安全系统设计基础第二周学习总结
- python将json转换为excel_使用python将Excel转换为JSON_python_酷徒编程知识库
- 计算机组成原理——计算机系统的性能指标(机器字长、存储容量、运算速度)
- 发红包案例(RedPacketFrame简介)
- spingMVC 请求参数绑定
- 开课吧:数据分析的价值体现在哪些方面?
- 广州地铁22号线将延伸至深圳,全线土建工程已完成57%
- 最详细的选型攻略!选择工业相机必须搞懂这10大要素!(建议收藏)
- win10找不到wifi网络_win10找不到realtek高清晰音频管理器的解决放法
- 你有必要不沾计算机一段时间英语,新人教版八年级英语下册unit 1必背词组及句子...
- 爱情在手机中升华-致我最亲爱的你们
- 如何使用MDK建立STM32H7双核编译工程
- 华为S7706升级到S7700-V200R008C00SPC500版本快速配置记录
- 贝塞尔曲线与贝塞尔曲面
- 创新科技成果广东功能性水稻品种 国稻种芯百团计划行动