gorm基本使用介绍

gorm基本使用介绍,第1张

文章目录 简介安装定义结构体结构体表名创建/更新时间 连接数据库连接数据库数据库连接池 create 建表建表检测表存在表更改 insert 插入插入举例 select 查询查询举例常见函数 update 更新更新举例常见函数更新表达式 delete 删除表连接查询belongs to(1v1)has one(1v1)has many(1v多)预加载 事务 *** 作自动事务手动事务 错误处理错误处理举例错误类型 Debug 调试直接执行查询

简介

是当今比较热门的 golang 的 orm *** 作数据库的工具,使用上主要是把 struct 类和数据库表进行映射, *** 作数据库时无需手写 sql。本质就是提供一组函数来帮助我们快速拼接 sql 语句,尽量减少 sql 的编写

github 地址:https://github.com/go-gorm/gorm

取数据逻辑:

从数据库读取的数据会先保存到预先定义的模型对象,然后我们就可以从模型对象得到我们想要的数据

存数据逻辑:

存数据到数据库也是先新建一个模型对象,然后把数据保存到模型对象,最后把模型对象保存到数据库 安装

必装:

mysql 驱动包

// mysql 驱动
go get -u gorm.io/driver/mysql

gorm 包

// gorm
go get -u gorm.io/gorm

基本 *** 作:

使用 struct 定义模型,主要在 golang 中表示 mysql 的表gorm 创建数据库连接gorm *** 作数据库 定义结构体 结构体

gorm默认使用 snake case,即 struct 中定义的变量名会自动识别成数据库表中下划线的列字段,比如说 CreateTime 就会被识别成数据表中的 create_time 列,所以需要在后面加上gorm:"column:createtime",gorm:"-"表示可以忽略该字段不参与数据表读写

// User 用户表
type User struct {
  ID int64
  Username string `gorm:"column:username"`
  Password string `gorm:"column:password"`
  // 时间戳
  CreateTime int64 `gorm:"column:createtime"`
}

gorm 提供了非常方便的 gorm.Model,当将其写在自定义的 struct 中时候,相当于类继承了 gorm.Model,gorm.Model 中存在 ID、CreatedAt、UpdatedAt、DeletedAt 默认 4 个字段

表名

可以在代码中添加 TableName 函数实现定义模型的表名;或者手动通过 gorm 的 Table 函数指定表名也不需要给模型定义 TableName 函数

func (u User) TableName() string {
	return "user"
}
创建/更新时间

gorm 约定使用 struct 中的 CreatedAt 和 UpdatedAt 表示创建时间和更新时间,所以 gorm 在创建和更新时候会自动填充,如果想用其他变量名就可以在gorm:"autoCreateTime:milligorm:"autoUpdateTime:milli

连接数据库 连接数据库

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

func main() {
  // 账号
  username := "root"
  // 密码
  password := "123456"
  // mysql 服务地址
  host := "127.0.0.1"
  // 端口
  port := 3306
  // 数据库名
  Dbname := "Dbname"
  
  // 拼接 mysql dsn,即拼接数据源,下方 {} 中的替换参数即可
  // {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=60s
  // timeout 是连接超时时间,readTimeout 是读超时时间,writeTimeout 是写超时时间,可以不填
  dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
  // 连接 mysql
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    panic("数据库连接失败, error" + err.Error())
  }
}
数据库连接池

在高并发中,为了提高数据库连接速度,避免重复连接数据库带来性能消耗,会常常使用数据库连接池来维护数据库连接。gorm 自带数据库连接池,只需要设置下数据库连接池参数即可

// 全局数据库 db
var db *gorm.DB

// 包初始化函数,可以用来初始化 gorm
func init() {
  // 配置 dsn
  
  // err
  var err error
  // 连接 mysql 获取 db 实例
  db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    panic("连接数据库失败, error=" + err.Error())
  }
  
  // 设置数据库连接池参数
  sqlDB, _ := db.DB()
  // 设置数据库连接池最大连接数
  sqlDB.SetMaxOpenConns(100)
  // 连接池最大允许的空闲连接数,如果没有sql任务需要执行的连接数大于20,超过的连接会被连接池关闭
  sqlDB.SetMaxIdleConns(20)
}

// 获取 gorm db,其他包调用此方法即可拿到 db
// 无需担心不同协程并发时使用这个 db 对象会公用一个连接,因为 db 在调用其方法时候会从数据库连接池获取新的连接
func GetDB() *gorm.DB {
	return db
}

使用例子

func main() {
  // 获取 db
  db := tools.GetDB()
  
  // 执行数据库查询 *** 作
}
create 建表 建表

AutoMigrate 支持建表,如果表存在则不会再创建,但是不支持字段修改删除,为避免数据意外丢失

// 建表
db.AutoMigrate(&User{})

// 建表
db.Migrator().CreateTable(&User{})

// 建 3 表
db.AutoMigrate(&User{}, &Product{}, &Order{})

// 可以通过 Set 设置附加参数,下面设置表的存储引擎为 InnoDB
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
检测表存在
// 检测User结构体对应的表是否存在
db.Migrator().HasTable(&User{})

// 检测表名users是否存在
db.Migrator().HasTable("users")
表更改
// 删除 User 结构体对应的表
db.Migrator().DropTable(&User{})

// 依据表名删除表
db.Migrator().DropTable("users")

// 修改字段
db.Migrator().DropColumn(&User{}, "Name")

// 为字段添加索引
db.Migrator().CreateIndex(&User{}, "Name")

// 修改索引名
db.Migrator().RenameIndex(&User{}, "Name", "Name2")

// 为字段删除索引
db.Migrator().DropIndex(&User{}, "Name")

// 检查索引存在
db.Migrator().HasIndex(&User{}, "Name")
insert 插入 插入举例

err := db.Create(&u).Error

// 连接数据库后获取一个 db

// 插入一条数据
// INSERT INTO `users` (`username`,`password`,`createtime`) VALUES ('abcnull','123456','1540824823')
if err := db.Create(&u).Error; err != nil {
  fmt.Println("插入失败", err)
	return
}
select 查询 查询举例

result := db.Where("username = ?", "abcnull").First(&u)

// 连接数据库后获取一个 db

// 定义一个保存数据的结构体
u := User{}

// 查询第一条数据
// SELECT * FROM `users` WHERE (username = 'abcnull') LIMIT 1
result := db.Where("username = ?", "abcnull").First(&u)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
  fmt.Println("找不到记录")
	return
}
常见函数

gorm 使用链式函数来查询

// Take 查询一条记录
// SELECT * FROM `user` LIMIT 1
db.Take(&u)

// First 根据主键 id 排序后的第一条
// SELECT * FROM `user` ORDER BY `id` LIMIT 1
db.First(&u)

// Last 根据主键 id 排序后最后一条
// SELECT * FROM `user` ORDER BY `id` DESC LIMIT 1
db.Last(&u)

// Find 查询多条记录,返回数组
// SELECT * FROM `user`
db.Find(&u)

// Pluck 提取列
// SELECT username FROM `user`
db.Model(&User{}).Pluck("username", &username)

// Where 表示条件,其中写 sql 部分
// SELECT * FROM `user` WHERE (id = '10') LIMIT 1
db.Where("id = ?", 10).Take(&user)

// Select 表示选择,其中写 sql 部分
// SELECT id,title FROM `user` WHERE `id` = '1' AND ((id = '1')) LIMIT 1
db.Select("id,title").Where("id = ?", 1).Take(&u)

// Order 表示排序方式,其中写 sql 部分
// SELECT * FROM `user` WHERE (create_time >= '2018-11-06 00:00:00') ORDER BY create_time desc
db.Where("create_time >= ?", "2018-11-06 00:00:00").Order("create_time desc").Find(&u)

// Limit Offset 分页常用
// SELECT * FROM `user` ORDER BY create_time desc LIMIT 10 OFFSET 0
db.Order("create_time desc").Limit(10).Offset(0).Find(&u)

// Count 计算行数
// SELECT count(*) FROM `user`
db.Model(Food{}).Count(&total)

// Group Having 分组查询,其中写 sql 部分,Group 必须和 Select 一起连用
// SELECT type, count(*) as total FROM `user` GROUP BY type HAVING (total > 0)
db.Model(User{}).Select("type, count(*) as  total").Group("type").Having("total > 0").Scan(&results)
update 更新 更新举例

db.Model(&u).Where("username = ?", "abcnull").Update("password", "654321")

// 连接数据库后获取一个 db

// 定义一个保存数据的结构体
u := User{}

// 更新用户表的密码
// UPDATE `users` SET `password` = '654321' WHERE (username = 'abcnull')
db.Model(&u).Where("username = ?", "abcnull").Update("password", "654321")
常见函数
// Save 更新函数
db.Save(&u)

// Update 更新某条记录的单个字段
db.Model(&u).Update("price", 25)

// Update 跟新所有记录的单个字段
db.Model(&User{}).Update("price", 25)

// Update 自定义条件而非主键更新某字段
db.Model(&User{}).Where("create_time > ?", "2018-11-06 20:00:00").Update("price", 25)

注意:Update 函数中还可以接受结构体或者 map 类型

更新表达式

比如 stock 要求变成 stock + 1 的形式该怎么办呢?

Update("stock", gorm.Expr("stock + 1"))
delete 删除

db.Where("username = ?", "abcnull").Delete(&u)

// 连接数据库后获取一个 db

// 定义一个保存数据的结构体
u := User{}

// 删除某条记录
// DELETE FROM `users`  WHERE (username = 'abcnull')
db.Where("username = ?", "abcnull").Delete(&u)
表连接查询 belongs to(1v1)

我们看下两表的表结构

// 用户表
type User struct {
  gorm.Model
  Name string
}

// 用户个性化信息表
type Profile struct {
  gorm.Model
  Name   string
  // 关联 User 表。这后头可以添加`gorm:"foreignkey:UserID"`,这里没添加是因为其命名格式就自动被识别为 User 外键
  User   User
  // 个性化信息表的外键,默认使用类型名+ID就可以被识别成外键
  UserID uint
}

我们再来看下如何进行关联查询

// profile 已经拿到一个实例

// 关联查询
db.Model(&profile).Association("User").Find(&user)
has one(1v1)

我们看下两表的结构体

// xyk
type CreditCard struct {
  gorm.Model
  Number string
  // 外键
  UserID uint
}

// 用户
type User struct {
  gorm.Model
  // xyk
  CreditCard CreditCard 
}

关联查询

// 关联查询
db.Model(&user).Association("CreditCard").Find(&card)
has many(1v多)

多个xyk id 对应一个 user

// xyk
type CreditCard struct {
  gorm.Model
  // 卡号
  Number   string
  // 默认外键, 用户Id
  UserID  uint
}

// 用户
type User struct {
  gorm.Model
  // 一对多关联属性,表示多张xyk
  CreditCards []CreditCard
}

例子

db.Model(&user).Association("CreditCard").Find(&user.CreditCard)
预加载

gorm 因为性能问题不会加载关联属性,可以通过 Preload 函数支持预加载

// 订单表
type Orders struct {
  gorm.Model
  // 外键
  UserID uint
  Price float64
}
// 用户表
type User struct {
  gorm.Model
  Username string
  // 关联订单,一对多关联关系
  Orders []Orders
}

// 预加载 Orders 字段值,Orders 字段是 User 的关联字段
db.Preload("Orders").Find(&users)

// 通过 Set 设置 gorm:auto_preload 属性,开启自动预加载,查询的时候才会自动完成关联查询
db.Set("gorm:auto_preload", true).Find(&users)
事务 *** 作 自动事务

通过 db.Transaction 函数实现事务 *** 作,此函数闭包返回错误,则回滚事物

db.Transction(func(tx *gorm.DB) error {
  // 在事务中插入数据
  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
    // 返回任何错误都会回滚事务
    return err
  }
  // 在事务中插入数据
  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
    // 返回任何错误都会回滚事务
    return err
  }
	// 返回 nil 提交事务
  return nil
})
手动事务

在一些和付费有关的系统中常常需要关注数据库 *** 作的原子性,比如说电商系统中减少库存和保存订单看成是一个原子 *** 作

// 开启事务
tx := db.Begin()

// 事务之更新 *** 作(库存减一),拿到影响的行数
rowsAffected := tx.Model(&food).Where("stock > 0").Update("stock", gorm.Expr("stock - 1")).RowsAffected
if rowsAffected == 0 {
  // 说明没有库存需要回滚
  tx.Rollback()
  return
}

// 事务之插入 *** 作,创建订单
err := tx.Create(&u).Error
if err != nil {
  tx.Rollback()
} else {
  tx.Commit()
}
错误处理 错误处理举例

gorm.DB中 *** 作数据库函数的.Error属性,Error 属性默认值是 nil,常见 *** 作是数据库 *** 作后我们使用Error.Is()来判定一下数据库 *** 作的错误类型是什么类型即可

// 举例
result := db.Where("username = ?", "abcnull").First(&u)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
  fmt.Println("找不到记录")
	return
}
错误类型

ErrRecordNotFound:查找不到记录时候的错误

Debug 调试

平常使用 db 来查询,但是如果使用 db.Debug() 则可以打印出来一条 sql 语句

result := db.Debug().Where("username = ?", "tizi365").First(&u)

其他

直接执行查询
// sql 语句,其中有多处 ?
sql := ""

// 原生执行 sql 语句绑定多个参数
db.Raw(sql, "")

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/989954.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存