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.Rowsql.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)相关推荐

  1. 3 GROM 关联 (golang)

    3 GROM 关联 属于 外键 关联外键 Belongs To 的使用 查询 查询 Where 条件 普通 SQL Struct & Map Not 条件 Or 条件 Inline Condi ...

  2. Kafka入门教程 Golang实现Kafka消息发送、接收

    一:核心概念 kafka是消息中间件的一种,是一种分布式流平台,是用于构建实时数据管道和流应用程序.具有横向扩展,容错,wicked fast(变态快)等优点. kafka中涉及的名词: 消息记录(r ...

  3. JavaTPoint 编程语言中文教程【翻译完成】

    原文:JavaTPoint 协议:CC BY-NC-SA 4.0 阶段:机翻(1) 危机只有发展到最困难的阶段,才有可能倒逼出有效的解决方案.--<两次全球大危机的比较研究> 在线阅读 在 ...

  4. LearnETutorial 中文系列教程【翻译完成】

    原文:LearnETutorial 协议:CC BY-NC-SA 4.0 人最大的痛苦就是说一些自己都不相信的话.--燕京学堂鹿会 在线阅读 在线阅读(Gitee) ApacheCN 学习资源 目录 ...

  5. golang实现webgis后端开发

    目录 前言 二.实现步骤 1.postgis数据库和model的绑定 2.将pg库中的要素转换为geojson (1)几何定义 (2)将wkb解析为几何类型 (3)定义geojson类型 (4)数据转 ...

  6. Go语言学习资料整理

    整理网上找到的Golang语言学习资料 基础 基础教程 书籍在线版 Go 指南-A Tour of Go Go语言圣经(中文版) Effective Go中文版 Go Web编程 build-web- ...

  7. bool类型数组转换成一个整数_Go 学习笔记 02 | 基本数据类型以及 byte 和 rune 类型...

    一.基本数据类型 unsafe.Sizeof() 查看不同长度的整型在内存中的存储空间. 类型转换,高位向低位转换要注意溢出. 数字字面量语法. 64 位系统中 Go 语言中浮点数默认是 float6 ...

  8. go语言和java比_Go VS Java:一位资深程序员对两种语言的解读

    导读:对于软件开发的编程语言,其实没有万能灵药. 本文作者详细介绍了他使用Java和Go这两种编程语言,一个是传统语言,一个是新兴语言的工作方式. Go VS Java 实话说,我很喜欢Java这门语 ...

  9. Ethereum Architecture : 以太坊架构

    Ethereum Architecture 去中心化背后的想法是不依赖单一/集中式服务器. 以太坊区块链到底是什么?区块链有两个主要组成部分: 数据库:网络中的每笔交易都存储在区块链中.当您部署应用程 ...

最新文章

  1. BZOJ3262 : 陌上花开
  2. ORA-04063: view SYS.DBA_REGISTRY has errors
  3. 【Linux】 Samba 服务器安装配置实现与Windows系统的文件共享服务
  4. mysql 录入窗体设计_在Access中,可用于设计输入界面的对象是   A)窗体 B)报表 C)查询 D)表...
  5. CTO俱乐部下午茶:技术团队管理中的那些事儿
  6. 信息安全系统设计基础第二周学习总结
  7. python将json转换为excel_使用python将Excel转换为JSON_python_酷徒编程知识库
  8. 计算机组成原理——计算机系统的性能指标(机器字长、存储容量、运算速度)
  9. 发红包案例(RedPacketFrame简介)
  10. spingMVC 请求参数绑定
  11. 开课吧:数据分析的价值体现在哪些方面?
  12. 广州地铁22号线将延伸至深圳,全线土建工程已完成57%
  13. 最详细的选型攻略!选择工业相机必须搞懂这10大要素!(建议收藏)
  14. win10找不到wifi网络_win10找不到realtek高清晰音频管理器的解决放法
  15. 你有必要不沾计算机一段时间英语,新人教版八年级英语下册unit 1必背词组及句子...
  16. 爱情在手机中升华-致我最亲爱的你们
  17. 如何使用MDK建立STM32H7双核编译工程
  18. 华为S7706升级到S7700-V200R008C00SPC500版本快速配置记录
  19. 贝塞尔曲线与贝塞尔曲面
  20. 创新科技成果广东功能性水稻品种 国稻种芯百团计划行动

热门文章

  1. 紫砂壶型和泥料适配茶叶
  2. 生成Kindle可读的mobi和PDF电子书
  3. ORA-31011:XML 语法分析失败
  4. Tampermonkey(油泼狓)插件的安装及使用
  5. Android SoftAP 实现框架
  6. 找不到/usr/bin/ld: 找不到 -lxxxx
  7. ArcGIS教程:创建网络数据集(一)
  8. Github使用教程(详细图文)
  9. low power-upf-vcsnlp(一)
  10. 数字排大小C语言怎么编写,C语言排列数字大小