网站首页 > 技术文章 正文
GORM 是 Go 语言的 ORM 包,功能强大,调用方便。GORM 有两个版本,V1和V2。遵循用新不用旧的原则,推荐使用最新的 V2 版本。
本文主要介绍使用GORM的 Auto Migration包括创建数据库,数据表,字段,字段类型,索引等操作。使用Auto Migration功能可以使项目启动时自己完成数据库的初始化操作。避免运维频繁修改数据库。公司如果有DBA或者只有一个数据库的不建议使用此方式。
创建数据库
以mysql为例:
使用标准库sql执行sql进行数据库创建,该sql先判断了是否存在数据库,不存在时创建,存在时则跳过。
注意:要执行创建数据库操作,分配的mysql用户需要具有相应权限。
//获取mysql连接信息
var dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/", config.Config.Mysql.UserName,
config.Config.Mysql.Password,
config.Config.Mysql.Host,
config.Config.Mysql.Port)
//创建数据库sql语句
createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", config.Config.Mysql.DBName)
// 创建数据库
if err = createDatabase(dsn, "mysql", createSql); err != nil {
log.WithFields(log.Fields{"err": err.Error()}).Panic(DriverMysql + "数据库创建失败")
return
}
//创建数据库
func createDatabase(dsn string, driver string, createSql string) error {
db, err := sql.Open(driver, dsn)
if err != nil {
return err
}
defer func(db *sql.DB) {
err = db.Close()
if err != nil {
fmt.Println(err)
}
}(db)
if err = db.Ping(); err != nil {
return err
}
_, err = db.Exec(createSql)
return err
}
连接数据库
使用上一步骤创建的数据库做mysql连接
//数据库驱动
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
config.Config.Mysql.UserName,
config.Config.Mysql.Password,
config.Config.Mysql.Host,
config.Config.Mysql.Port,
config.Config.Mysql.DBName,
)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.WithFields(log.Fields{"err": err.Error()}).Panic(DriverMysql + "数据库连接失败")
return
}
// 获取通用数据库对象 sql.DB ,然后使用其提供的功能
sqlDB, err := db.DB()
if err != nil {
log.WithFields(log.Fields{"err": err.Error()}).Panic(DriverMysql + "数据库连接失败")
return
}
err = sqlDB.Ping()
if err != nil {
log.WithFields(log.Fields{"err": err.Error()}).Panic(DriverMysql + "数据库连接失败")
return
}
// SetMaxIdleConns 设置MySQL的最大空闲连接数。
sqlDB.SetMaxIdleConns(config.Config.Mysql.MinConns)
// SetMaxOpenConns 设置MySQL的最大连接数。
sqlDB.SetMaxOpenConns(config.Config.Mysql.MaxConns)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Second * 30)
log.Info("init db success")
初始化表
首先定义一个 GORM 模型(Model),Model 是标准的 Go struct,用来代表数据库中的一个表结构。我们可以给 Model 添加 TableName 方法,来告诉 GORM 该 Model 映射到数据库中的哪张表。Model 定义如下:
type BaseModel struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
// Role 角色表
type Role struct {
BaseModel
Name string `json:"name" gorm:"column:name;comment:名称;type:varchar(64);NOT NULL;"`
Status int `json:"status" form:"status" gorm:"column:status;default:1;comment:状态;type:tinyint(3);"`
}
func (Role) TableName() string {
return "role"
}
BaseModel跟gorm.Model基本一致
注册表:
需要注意,如果需要使用事务,则设置db.Set("gorm:table_options", "ENGINE=InnoDB")。创建InnoDB类型的表,否则默认创建MyISAM引擎表,不支持事务。
func registerTables(db *gorm.DB) {
err := db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(
//系统模块表
entity.Role{},
entity.User{},
entity.Config{},
entity.SysLog{},
//自动化模块表
)
if err != nil {
log.WithFields(log.Fields{"err": err.Error()}).Panic("注册数据库表失败")
}
}
GORM 的 AutoMigrate 方法,为了安全只对新增的字段或索引进行变更。后续添加字段只需要在Model结构体新写一个字段即可。每次程序启动时都会自动比对表结构,对新增的进行变更。
初始化表数据
有些数据需要在创建系统的时候就存在,好比说角色信息。可以在此处进行添加。大体步骤,先判断是否已存在数据,已存在则跳过;不存在则插入数据。
先声明一个初始化表数据接口,里面提供两个方法
type InitTable interface {
DataInserted(*gorm.DB) bool //表数据是否已经插入
InitializeData(*gorm.DB) (err error) //初始化表数据
}
主函数:
func initTableData(db *gorm.DB) (err error) {
var initTables []InitTable
initTables = append(initTables,
//此处添加需要初始化的实现表
InitRole{},
InitUser{},
InitConfig{},
)
for _, init := range initTables {
if init.DataInserted(db) {
//表数据是否已存在
continue
}
//初始化表数据
if err = init.InitializeData(db); err != nil {
return err
}
}
//更新数据,目前只有一条,在此单独写入
err = InitConfig{}.Update(db)
return
}
每个需要初始化数据的表,单独实现InitTable接口。这里以Role表为例:
type InitRole struct {
}
// DataInserted
// @description: 数据是否已插入
// @param:
// @author: GJing
// @email: gjing1st@gmail.com
// @date: 2022/10/31 18:19
// @success:
func (i InitRole) DataInserted(db *gorm.DB) bool {
if errors.Is(db.Where(entity.Role{}.TableName()+".id = ?", dict.RoleIdAdmin).First(&entity.Role{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据
return false
}
return true
}
// InitializeData
// @description: 初始化数据
// @param:
// @author: GJing
// @email: gjing1st@gmail.com
// @date: 2022/10/31 17:26
// @success:
func (i InitRole) InitializeData(db *gorm.DB) (err error) {
if db == nil {
return global.DBNullErr
}
entities := []entity.Role{
{Name: "超管", BaseModel: entity.BaseModel{ID: dict.RoleIdSuperAdmin}},
{Name: "管理员", BaseModel: entity.BaseModel{ID: dict.RoleIdAdmin}},
{Name: "操作员", BaseModel: entity.BaseModel{ID: dict.RoleIdOperator}},
}
if err = db.Create(&entities).Error; err != nil {
return global.InitDataErr
}
return
}
至此,对数据库的创建以及表和表结构的变动,全部由gorm完成。免去了部署和升级对数据库的运维。
创建复合唯一索引
有时我们需要创建唯一索引字段,但是用了DeletedAt软删除,再次添加相同的字段值时会报错唯一索引错误。
gorm提供跟DeletedAt组合的唯一索引,使用方式如下:
import (
"gorm.io/plugin/soft_delete"
"time"
)
// User 用户表
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `json:"name" gorm:"column:name;comment:用户名;uniqueIndex:user_name;type:varchar(64);NOT NULL;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt soft_delete.DeletedAt `gorm:"uniqueIndex:user_name" json:"-"`
}
使用name用户名和DeletedAt作为唯一索引。此时DeletedAt是uint类型
GORM 官方文档
猜你喜欢
- 2024-09-09 混合云资产管理项目(二)(混合云存储产品有哪些)
- 2024-09-09 Go语言进阶之Go语言高性能Web框架Iris项目实战-完善用户管理EP04
- 2024-09-09 数据库与 Go 的交互(go数据库和kegg数据库)
- 2024-09-09 七爪源码:N+1 查询如何烧毁您的数据库
- 2024-09-09 Go的安全编程和防御性编程(防止代码注入)
- 2024-09-09 Vue3+Go 仿抖音项目架构设计与实现
- 2024-09-09 腾讯Go安全指南(腾讯官网最新安全公告)
- 2024-09-09 Grails指南24查询高阶(grails中文参考手册)
- 2024-09-09 Redis优化高并发下的秒杀性能(redis秒杀高并发代码)
- 2024-09-09 10.Go语言编写个人博客 文章分类(基于golang的个人博客系统)
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)