前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go(五)不知道怎么用Gorm?

Go(五)不知道怎么用Gorm?

作者头像
lomtom
发布2021-12-13 19:09:31
2K0
发布2021-12-13 19:09:31
举报
文章被收录于专栏:博思奥园

前言

所有的后端应用都离不开数据库的操作,在Go中也有一些好用的数据库操作组件,例如Gorm就是一个很不错的选择。

这里是Gorm自己例举的优点:

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

当然,你可能用不到gorm这么多特性,但是也不阻碍GormGo中一个非常优秀的ORM框架。

本文也不探究Gorm和其他框架的优劣比较,而是从使用者出发,一起来探讨Gorm在实际开发中的使用。

当然Gorm本身的官方文档已经非常详细了,如果对本文中的部分Gorm使用有稍许疑惑的话,请移步官方文档:https://gorm.io/zh_CN/docs/index.html

安装

在控制台执行go get命令进行安装依赖,驱动根据自己的实际使用进行安装,这里以MySQL为例。

Gorm官方支持的数据库类型有:MySQL, PostgreSQL, SQlite, SQL Server

代码语言:javascript
复制
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

在使用时引入依赖即可

代码语言:javascript
复制
import (
 "gorm.io/driver/mysql"
 "gorm.io/gorm"
)

建立连接

使用Gorm建立数据库的连接其实很简单,但是要做到好用,那就需要花点心思,在这里,将带领大家怎么从最简单的连接到好用的连接设置。

最基本的连接

代码语言:javascript
复制
func GetDb() *gorm.DB {
 // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
 if err!= nil {
  return nil
 }
 return db
}

“注意:

  1. 想要正确的处理 time.Time ,您需要带上 parseTime 参数,
  2. 使用charset指定编码,要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4

更多参数设置:https://github.com/go-sql-driver/mysql#parameters ”

设置连接池

Gorm同样支持连接池,Gorm使用 database/sql 维护连接池

分别使用SetMaxIdleConnsSetMaxOpenConnsSetConnMaxLifetime来设置最大空闲连接数、最大连接数和设置连接空闲超时参数。

代码语言:javascript
复制
func GetDb() *gorm.DB {
 // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{

 })
 if err != nil {
  return nil
 }
 sqlDB, err := db.DB()
 if err != nil {
  log.Printf("database setup error %v", err)
 }
 sqlDB.SetMaxIdleConns(10)           //最大空闲连接数
 sqlDB.SetMaxOpenConns(100)          //最大连接数
 sqlDB.SetConnMaxLifetime(time.Hour) //设置连接空闲超时
 return db
}

全局连接

为了方便使用,我们可以在一开始就使用一个全局变量来保存数据库的连接,在使用时直接调用即可,而不需要再次进行数据库的初始化。

代码语言:javascript
复制
var db *gorm.DB

// GetDb 获取连接
func GetDb() *gorm.DB {
 return db
}

将之前的函数改为给db进行初始化并赋值,在使用的时候直接调用GetDb函数即可

代码语言:javascript
复制
func DbInit(){
 // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
 tempDb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{

 })
 if err != nil {
  return nil
 }
 sqlDB, err := tempDb.DB()
 if err != nil {
  log.Printf("database setup error %v", err)
 }
 sqlDB.SetMaxIdleConns(10)           //最大空闲连接数
 sqlDB.SetMaxOpenConns(100)          //最大连接数
 sqlDB.SetConnMaxLifetime(time.Hour) //设置连接空闲超时
 db = tempDb
}

利用配置文件

到这里,你其实发现已经能够很好的使用Gorm去建立数据库连接了,但是有没有什么办法像Spring Boot一样从配置文件中获取连接参数呢,恰好第三章中讲到了怎么使用读取配置文件的方法,那何不利用起来呢?

戳 -> Go(三)Go配置文件

在配置文件中定义数据库连接参数

代码语言:javascript
复制
database:
  type: mysql
  host: localhost
  port: 3306
  username: root
  password: 123456
  dbname: test
  max_idle_conn: 10
  max_open_conn: 30
  conn_max_lifetime: 300

定义相应的结构体

代码语言:javascript
复制
var Database *database

type conf struct {
 DB   database  `yaml:"database"`
}

type database struct {
 Type   string  `yaml:"type"`
 Host      string     `yaml:"host"`
 Port      string     `yaml:"port"`
 UserName   string   `yaml:"username"`
 Password   string   `yaml:"password"`
 DbName      string     `yaml:"dbname"`
 MaxIdleConn  int   `yaml:"max_idle_conn"`
 MaxOpenConn  int   `yaml:"max_open_conn"`
 ConnMaxLifetime int   `yaml:"conn_max_lifetime"`
}

具体怎么绑定参数,请戳 -> Go(三)Go配置文件

为了更直观的感受,将URI抽取出来

代码语言:javascript
复制
//获取链接URIfunc mySQLUri() string { return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true",  Database.UserName,  Database.Password,  Database.Host,  Database.Port,  Database.DbName)}

那么最终呈现的就是这样。

代码语言:javascript
复制
var db *gorm.DB// GetDb 获取连接func GetDb() *gorm.DB { return db}// DbInit 数据库连接池初始化func DbInit() { fmt.Println(mySQLUri()) conn, err1 := gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{}) if err1 != nil {  log.Printf("connect get failed.")  return } sqlDB, err := conn.DB() if err != nil {  log.Printf("database setup error %v", err) } sqlDB.SetMaxIdleConns(Database.MaxIdleConn)                                     //最大空闲连接数 sqlDB.SetMaxOpenConns(Database.MaxOpenConn)                                     //最大连接数 sqlDB.SetConnMaxLifetime(time.Duration(Database.ConnMaxLifetime) * time.Second) //设置连接空闲超时 db = conn}

如果想要在项目启动时自动初始化,将DbInit方法名改为init即可,否则,需要在main方法中自行调用执行初始化。

为了更好的开发,我们可以自定义Gorm的日志

代码语言:javascript
复制
//初始化数据库日志newLogger := logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer logger.Config{  SlowThreshold:             time.Second, // Slow SQL threshold  LogLevel:                  logger.Info, // Log level  IgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for logger  Colorful:                  true,        // Disable color },)

将其作为参数放置在Gorm参数上gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{})

代码语言:javascript
复制
conn, err1 := gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{ Logger: newLogger,})

使用

GormCURD相对来说也比较简单。

定义一个结构体User,除开记录的字段,有编号、姓名、密码三个字段

代码语言:javascript
复制
type User struct { Id    int64 `gorm:"primaryKey;column:id;"` Username  string  `gorm:"column:user_name;type:varchar(255);default:(-)" ` Password  string  `gorm:"column:password;type:varchar(255);default:(-)"` Deleted    gorm.DeletedAt `gorm:"column:deleted;type:timestamp;default:(-)"`         CreateTime time.Time      `gorm:"column:create_time;type:timestamp;default:(-)"` UpdateTime time.Time      `gorm:"column:update_time;type:timestamp;default:(-)"`}// TableName 自定义表名func (*User) TableName() string { return "users"}

说明:

  1. 使用primaryKey指定主键
  2. 使用column:id指定在数据库中的列名
  3. 使用gorm.DeletedAt标明该字段为删除标志,如果使用了gorm.DeletedAt,数据库列类型必须为时间格式。
  4. 使用type:varchar(255)标明字段类型
  5. 使用default:(-)设置默认值,-表示为无默认值。
  6. 使用User.TableName表名数据库名,当使用Model绑定结构体时,Gorm会默认调用该方法,除此之外,还可以使用db.Table("user")显式的标明表名。

查询

  1. 获取第一个,默认查询第一个
代码语言:javascript
复制
// GetFirst SELECT * FROM users ORDER BY id LIMIT 1;func GetFirst() (user *User) { db := config.GetDb() db.Model(&user).First(&user) return }
  1. 获取最后一个
代码语言:javascript
复制
// GetLast SELECT * FROM users ORDER BY id DESC LIMIT 1;func GetLast() (user *User) { db := config.GetDb() db.Model(&user).Last(&user) return}
  1. 通过主键获取
代码语言:javascript
复制
// GetById SELECT * FROM users WHERE id = 1;func GetById(id int64) (user *User) { db := config.GetDb() db.Model(&user).Find(&user,id) return}等同于func GetById(id int64) (user *User) { db := config.GetDb() db.Model(&user).Where("id = ?",id).Find(&user) return}
  1. 通过主键批量查询
代码语言:javascript
复制
// GetByIds SELECT * FROM users WHERE id IN (1,2,3);func GetByIds(ids []int64) (user []*User) { db := config.GetDb() db.Model(&user).Find(&user,ids) return}等同于func GetByIds(s []int64) (user []*User) { db := config.GetDb() db.Model(&user).Where("id in ?",ids).Find(&user) return}
  1. 获取部分参数,例如只获取名字和密码
代码语言:javascript
复制
// GetSomeParam SELECT username,password FROM users WHERE id = 1;func GetSomeParam(id int64) (user *User) { db := config.GetDb() db.Model(&user).Select("username", "password").Find(&user,id) return}
  1. 分页查询,可以使用Limit & Offset进行分页查询
代码语言:javascript
复制
// GetPage SELECT * FROM users OFFSET 5 LIMIT 10;func GetPage(limit int,offset int) (user []*User) { db := config.GetDb() db.Model(&user).Limit(limit).Offset(offset).Find(&user) return}
  1. order
代码语言:javascript
复制
// GetByOrder SELECT * FROM users ORDER BY id desc, username;func GetByOrder() (user []*User) { db := config.GetDb() db.Model(&user).Order("id desc,username").Find(&user) return}等同于func GetByOrder() (user []*User) { db := config.GetDb() db.Model(&user).Order("id desc").Order("username").Find(&user) return}

更多请移步:https://gorm.io/zh_CN/docs/query.html

新增

  1. 创建单个(Create)
代码语言:javascript
复制
func Create(user *User)  { db := config.GetDb() db.Model(&user).Create(&user) return}
  1. 保存单个(Save)
代码语言:javascript
复制
func Save(user *User)  { db := config.GetDb() db.Model(&user).Save(&user) return}

CreateSave的区别:Save需要插入的数据存在则不进行插入,Create无论什么情况都执行插入

  1. 创建多个
代码语言:javascript
复制
func CreateBatch(user []*User)  { db := config.GetDb() db.Model(&user).Create(&user) return}

更多请移步:https://gorm.io/zh_CN/docs/create.html

修改

  1. 更新单个字段
代码语言:javascript
复制
// UpdateUsername UPDATE users SET username = "lomtom" where id = 1func UpdateUsername(id int64,username string)  { db := config.GetDb() db.Model(&User{}).Where("id = ?",id).Update("username",username) return}
  1. 全量/多列更新(根据结构体)
代码语言:javascript
复制
// UpdateByUser UPDATE `user` SET `id`=14,`user_name`='lomtom',`password`='123456',`create_time`='2021-09-26 14:22:21.271',`update_time`='2021-09-26 14:22:21.271' WHERE id = 14 AND `user`.`deleted` IS NULLfunc UpdateByUser(user *User)  { db := config.GetDb() db.Model(&User{}).Where("id = ?",user.Id).Updates(&user) return}

更多请移步:https://gorm.io/zh_CN/docs/update.html

删除

  1. 简单删除(根据user里的id进行删除)
代码语言:javascript
复制
// DeleteByUser DELETE from users where id = 28;// DeleteByUser UPDATE `user` SET `deleted`='2021-09-26 14:25:33.368' WHERE `user`.`id` = 28 AND `user`.`deleted` IS NULLfunc DeleteByUser(user *User)  { db := config.GetDb() db.Model(&User{}).Delete(&user) return}

说明:结构体未加gorm.DeletedAt标记的字段,直接删除,加了将更新deleted字段,即实现软删除

  1. 根据id进行删除
代码语言:javascript
复制
// DeleteById UPDATE `user` SET `deleted`='2021-09-26 14:29:55.15' WHERE `user`.`id` = 28 AND `user`.`deleted` IS NULLfunc DeleteById(id int64)  { db := config.GetDb() db.Model(&User{}).Delete(&User{},id) return}

事务

同样,Gorm也有丰富的事务支持。

匿名事务

可使用db.Transaction匿名方法来表明多个操作在一个事务里面,返回err将回滚,返回nil将提交事务

代码语言:javascript
复制
func Transaction() error { db := config.GetDb() err := db.Transaction(func(tx *gorm.DB) error {  // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')  if err := tx.Create(&User{Username: "lomtom"}).Error; err != nil {   // 返回任何错误都会回滚事务   return err  }  if err := tx.Delete(&User{}, 28).Error; err != nil {   return err  }  // 返回 nil 提交事务  return nil }) if err != nil {  return err } return nil}

手动事务

db.Begin()表明一个事务的开始,出现错误使用tx.Rollback(),事务提交使用tx.Commit()

代码语言:javascript
复制
func Transaction1() error { db := config.GetDb() tx := db.Begin() defer func() {  if r := recover(); r != nil {   tx.Rollback()  } }() // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') if err := tx.Create(&User{Username: "lomtom"}).Error; err != nil {  // 回滚事务  tx.Rollback()  return err  } if err := tx.Delete(&User{}, 28).Error; err != nil {  tx.Rollback()  return err } // 提交事务 return tx.Commit().Error}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 博思奥园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装
  • 建立连接
    • 最基本的连接
      • 设置连接池
        • 全局连接
          • 利用配置文件
          • 使用
            • 查询
              • 新增
                • 修改
                  • 删除
                  • 事务
                    • 匿名事务
                      • 手动事务
                      相关产品与服务
                      数据库
                      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档