🏷️个人主页:鼠鼠我捏,要死了捏的主页🏷️系列专栏: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库
go get -u gorm.io/gorm
安装mysql驱动
go get -u gorm.io/driver/mysql
连接url的组成
[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)
mysql://root:123123@tcp(127.0.0.1:3306)/testName?charset=utf8mb4&parseTime=True&loc=Local
2.建立连接
自定义mysql配置
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配置
gormConfig := &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
// 使用自定义的logger,gorm默认是将日志输出控制台,使用自定义logger可保存日志到文件
Logger: getGormLogger(),
}
调用open对象建立连接,获取连接客户端
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进行表结构同步(根据定义的结构体创建/修改表结构,保证数据结构的一致性,省去手动建表的步骤)
/*
数据库表结构同步,该方法只会增加新的字段或修改字段,不会删除字段,
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()
添加一条数据,不能重复添加
user := User{Name: "jinzhu"}
db.Create(&user)
user := User{Name: "jinzhu"}
db.Model(&user).Create(&user)
Save()
添加一条数据,若已存在,则更新对应数据
user := User{Name: "jinzhu"}
db.Save(&user)
CreateInBatches(records, batchSize)
批量添加数据
- records:要插入的记录的切片或结构体
- batchSize:每个批量添加的数量,避免一次性插入太多记录对数据库造成压力
users := []User{
{Name: "jinzhu"},
{Name: "hello"},
}
db.CreateInBatches(users, 10)
删除
Delete()
根据结构体Id删除一条数据,若定义了DeletedAt字段,则会进行软删除
db.Delete(&user)
db.Model(&user).Delete(&User{})
.Unscoped().Delete()
忽略软删除直接删除一条数据
db.Unscoped().Delete(&user)
Where().Delete()
根据条件删除多条记录
db.Where("name = ?", "jinzhu").Delete(&User{})
修改
db.Save()
根据结构体主键更新数据
user.Name = "updated"
db.Save(&user)
db.Model().Update()
通过模型对象更新指定字段
db.Model(&user).Update("name", "new_name")
db.Updates()
批量更新结构体中的字段
db.Model(&user).Updates(User{Name: "new"})
db.UpdateColumn()
更新单个字段
db.Model(&user).UpdateColumn("name", "new")
db.Where().Update()
根据条件更新多条记录
db.Where("id=?", 1).Update("name", "updated")
查询
db.First()
查询第一条记录
var user User
db.First(&user, 1) // id = 1
db.Where().Find()
条件查询多条记录
db.Where("name = ?", "jinzhu").Find(&users)
db.Order()
排序查询
db.Order("age desc").Find(&users)
db.Limit()
限制返回条数
db.Limit(10).Find(&users)
db.Offset()
分页查询
db.Offset(20).Limit(10).Find(&users)
db.Count()
统计记录数
var count int
db.Find(&users).Count(&count)
事务
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关联起来。
// 要使用预加载函数的结构体
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"`
}
使用预加载函数:
err = tx.Preload("Struct1").Preload("Struct2").Find(&problemList).Error
预加载中省略某字段:
err = tx.Preload("Struct1", func(db *gorm.DB) *gorm.DB {
return db.Omit("省略的字段名")
}).Preload("Struct2").Find(&problemList).Error
Omit():查询中省略指定字段
tx := DB.Model(&model.ProblemBasic{}).Omit("content")
5.格式化时间字段
当我们使用gorm时,往往需要格式化时间字段来满足使用需求
自定义时间类型,重写其方法
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对其进行了封装,使得数据库操作变得更加简单和灵活。
版权归原作者 鼠鼠我捏,要死了捏 所有, 如有侵权,请联系我们删除。