探索Gorm - Golang流行的数据库ORM框架

🏷️个人主页鼠鼠我捏,要死了捏的主页

🏷️系列专栏Golang全栈-专栏

🏷️个人学习笔记,若有缺误,欢迎评论区指正

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie

前言

我们在之前的专栏中讲解了在Go中如何使用原生的database/sql库完成数据库编程,今天我们继续来了解一下一款在go中常用的数据库ORM框架-Gorm(GORM - The fantastic ORM library for Golang, aims to be developer friendly.

ORM(Object-Relational Mapping)是一种编程技术,它将对象和关系数据库之间的映射抽象出来,使得开发者可以通过面向对象的方式操作数据库,而不用直接处理SQL语句,相当于在业务逻辑层和数据库层之间一座桥梁。

Gorm是一款用于Golang的ORM框架,它提供了丰富的功能,包括模型定义、数据验证、关联查询等。 Gorm的设计目标是简洁而强大,使得开发者能够更轻松地进行数据库操作


目录

前言

1.安装与配置

安装gorm库

安装mysql驱动

连接url的组成

2.建立连接

自定义mysql配置

自定义gorm配置

调用open对象建立连接,获取连接客户端

扩展:使用AutoMigrate进行表结构同步(根据定义的结构体创建/修改表结构,保证数据结构的一致性,省去手动建表的步骤)

3.基本使用

添加

Create()

Save()

[CreateInBatches(records, batchSize)](#CreateInBatches(records, batchSize))

删除

Delete()

.Unscoped().Delete()

Where().Delete()

修改

db.Save()

db.Model().Update()

db.Updates()

db.UpdateColumn()

db.Where().Update()

查询

db.First()

db.Where().Find()

db.Order()

db.Limit()

db.Offset()

db.Count()

事务

4.其他常用方法

Preload():预加载

目的:减少sql查询次数,避免N+1问题,减少内存开销,适用于复杂关系

基本使用:

Omit():查询中省略指定字段

5.格式化时间字段

总结


1.安装与配置

安装gorm库
bash 复制代码
go get -u gorm.io/gorm
安装mysql驱动
bash 复制代码
go get -u gorm.io/driver/mysql
连接url的组成
bash 复制代码
[DRIVER]://[USER]:[PASS]@[IP]:[PORT]/[DBNAME]?charset=utf8&parseTime=True&loc=Local
  • DRIVER: 数据库驱动名称,如mysql,postgres,sqlite3等

  • USER: 数据库用户名

  • PASS: 数据库密码

  • IP: 数据库主机IP地址,可以是localhost或127.0.0.1等

  • PORT: 数据库端口,默认为3306

  • DBNAME: 数据库名称

  • charset: 字符集,默认utf8

  • parseTime: 是否解析时间字段,默认True

  • loc: 时区,默认Local

示例(mysql连接url)

bash 复制代码
mysql://root:123123@tcp(127.0.0.1:3306)/testName?charset=utf8mb4&parseTime=True&loc=Local

2.建立连接

自定义mysql配置
Go 复制代码
mysqlConfig := mysql.Config{
    DSN:                       dsn,   // 连接url
    DefaultStringSize:         255,   // string 类型字段的默认长度
    DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
    DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
    DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
    SkipInitializeWithVersion: false, // 根据版本自动配置
}
自定义gorm配置
Go 复制代码
gormConfig := &gorm.Config{
    DisableForeignKeyConstraintWhenMigrating: true,
    // 使用自定义的logger,gorm默认是将日志输出控制台,使用自定义logger可保存日志到文件
    Logger:                                   getGormLogger(), 
}
调用open对象建立连接,获取连接客户端
Go 复制代码
if db, err := gorm.Open(mysql.New(mysqlConfig), gormConfig); err != nil {
		global.App.Log.Error("mysql connect failed, err:", zap.Any("err", err))
		return nil
	} else {
		global.App.Log.Info("mysql数据库连接成功~~~~~")
		sqlDB, _ := db.DB()
		sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns)
		sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns)
		//数据库表结构同步
		initMySqlTables(db)
		return db
	}
扩展:使用AutoMigrate进行表结构同步(根据定义的结构体创建/修改表结构,保证数据结构的一致性,省去手动建表的步骤)
Go 复制代码
/*
数据库表结构同步,该方法只会增加新的字段或修改字段,不会删除字段,
AutoMigrate 方法会尝试自动更改表结构,以使其与结构体定义保持一致
*/
func initMySqlTables(db *gorm.DB) {
    err := db.AutoMigrate(
       // 需要同步的表的结构体放里面
       model.User{},
       model.Media{},
    )
    if err != nil {
       global.App.Log.Error("migrate table failed", zap.Any("err", err))
       // 退出程序
       os.Exit(0)
    }
}

3.基本使用

添加

Create()

添加一条数据,不能重复添加

Go 复制代码
user := User{Name: "jinzhu"}
db.Create(&user) 
Go 复制代码
user := User{Name: "jinzhu"}
db.Model(&user).Create(&user)
Save()

添加一条数据,若已存在,则更新对应数据

Go 复制代码
user := User{Name: "jinzhu"}
db.Save(&user) 
CreateInBatches(records, batchSize)

批量添加数据

  • records:要插入的记录的切片或结构体
  • batchSize:每个批量添加的数量,避免一次性插入太多记录对数据库造成压力
Go 复制代码
users := []User{
  {Name: "jinzhu"},
  {Name: "hello"},
} 
db.CreateInBatches(users, 10)

删除

Delete()

根据结构体Id删除一条数据,若定义了DeletedAt字段,则会进行软删除

Go 复制代码
db.Delete(&user)
Go 复制代码
db.Model(&user).Delete(&User{})
.Unscoped().Delete()

忽略软删除直接删除一条数据

Go 复制代码
db.Unscoped().Delete(&user)
Where().Delete()

根据条件删除多条记录

Go 复制代码
db.Where("name = ?", "jinzhu").Delete(&User{})

修改

db.Save()

根据结构体主键更新数据

Go 复制代码
user.Name = "updated"
db.Save(&user) 
db.Model().Update()

通过模型对象更新指定字段

Go 复制代码
db.Model(&user).Update("name", "new_name")
db.Updates()

批量更新结构体中的字段

Go 复制代码
db.Model(&user).Updates(User{Name: "new"}) 
db.UpdateColumn()

更新单个字段

Go 复制代码
db.Model(&user).UpdateColumn("name", "new")
db.Where().Update()

根据条件更新多条记录

Go 复制代码
db.Where("id=?", 1).Update("name", "updated")

查询

db.First()

查询第一条记录

Go 复制代码
var user User
db.First(&user, 1) // id = 1 
db.Where().Find()

条件查询多条记录

Go 复制代码
db.Where("name = ?", "jinzhu").Find(&users)
db.Order()

排序查询

Go 复制代码
db.Order("age desc").Find(&users) 
db.Limit()

限制返回条数

Go 复制代码
db.Limit(10).Find(&users)
db.Offset()

分页查询

Go 复制代码
db.Offset(20).Limit(10).Find(&users)
db.Count()

统计记录数

Go 复制代码
var count int
db.Find(&users).Count(&count)

事务

Go 复制代码
tx := db.Begin() //开启事务
tx.Create(&user)
tx.Commit() //提交事务
tx.Rollback() //回滚事务

4.其他常用方法

Preload():预加载

目的:减少sql查询次数,避免N+1问题,减少内存开销,适用于复杂关系
基本使用:

结构体定义:gorm:"foreignKey:problem_id;references:id"指明关联关系,gorm会根据该关系进行预加载。如下面例子中把Struct111的problem_id与ProblemBasic的id关联起来。

Go 复制代码
// 要使用预加载函数的结构体
type ProblemBasic struct {
    ID                uint               `gorm:"primarykey;" json:"id"`
    
    //  需要预加载的数据
    Struct1 []*Struct111 `gorm:"foreignKey:problem_id;references:id" json:"problem_categories"` 
    Struct2 *Struct2222 `gorm:"foreignKey:problem_id;references:id;" json:"test_cases"`             
}

使用预加载函数:

Go 复制代码
err = tx.Preload("Struct1").Preload("Struct2").Find(&problemList).Error

预加载中省略某字段:

Go 复制代码
err = tx.Preload("Struct1", func(db *gorm.DB) *gorm.DB {
    return db.Omit("省略的字段名")
}).Preload("Struct2").Find(&problemList).Error

Omit():查询中省略指定字段

Go 复制代码
tx := DB.Model(&model.ProblemBasic{}).Omit("content")

5.格式化时间字段

当我们使用gorm时,往往需要格式化时间字段来满足使用需求

自定义时间类型,重写其方法

Go 复制代码
type LocalTime time.Time

// 定义 LocalTime 类型在 JSON 编码时的行为
func (t *LocalTime) MarshalJSON() ([]byte, error) {
	tTime := time.Time(*t)
	return []byte(fmt.Sprintf("\"%v\"", tTime.Format("2006-01-02 15:04:05"))), nil
}

// Value方法即在存储时调用,将该方法的返回值进行存储,该方法可以实现数据存储前对数据进行相关操作
func (t LocalTime) Value() (driver.Value, error) {
   	var zeroTime time.Time
   	tlt := time.Time(t)
   	//判断给定时间是否和默认零时间的时间戳相同
   	if tlt.UnixNano() == zeroTime.UnixNano() {
   		return nil, nil
   	}
   	return tlt, nil
}

// Scan方法可以实现在数据查询出来之前对数据进行相关操作
func (t *LocalTime) Scan(v interface{}) error {
   	if value, ok := v.(time.Time); ok {
   		*t = LocalTime(value)
   		return nil
   	}
   	return fmt.Errorf("can not convert %v to timestamp", v)
}

总结

Gorm是一款用于Golang的ORM框架,它提供了丰富的功能,包括模型定义、数据验证、关联查询等。Go提供的原生库已经能够很好的完成数据库编程,而Gorm对其进行了封装,使得数据库操作变得更加简单和灵活。

相关推荐
无限进步_4 分钟前
扫雷游戏的设计与实现:扫雷游戏3.0
c语言·开发语言·c++·后端·算法·游戏·游戏程序
追逐时光者15 分钟前
使用 Visual Studio 快速创建 NuGet 程序包并发布到 NuGet 官网
后端·.net·visual studio
撩得Android一次心动16 分钟前
Android adb 基础使用指南
android·adb
xiyangyang811043 分钟前
ubantu的adb命令(首次安装adb)
adb
江湖人称小鱼哥1 小时前
Prisma 命令安全指南
数据库·安全·prisma
ahauedu1 小时前
Spring Boot 2.7+ 中 RedisConnectionFactory Autowire 警告的深度解析
java·spring boot·后端
Gauss松鼠会1 小时前
【openGauss】1分钟掌握:openGauss活动会话CPU占用率获取
数据库·database·opengauss
豆沙沙包?1 小时前
2025年--Lc182--sql(排序和分组)--Java版
java·数据库·sql
CoLiuRs2 小时前
异步任务使用场景与实践
微服务·性能优化·golang