1. 介绍
gorm
是一个使用Go
语言编写的ORM
框架。 它文档齐全,对开发者友好,支持主流数据库。具体使用可参考之前的文章Go常用包(十九):全功能ORM框架(gorm)
1.1 集成流程
1.2 涉及目录
2. 配置
2.1 编辑主配置./config.yaml
mysql: host: 127.0.0.1 port: 3306 user: root password: root database: test charset: utf8mb4 parseTime: true timeZone: Local defaultStringSize: 255 disableDatetimePrecision: true skipInitializeWithVersion: false autoMigrate: true slowSql: 50ms logLevel: info ignoreRecordNotFoundError: true gorm: skipDefaultTx: false tablePrefix: "app_" singularTable: true coverLogger: true prepareStmt: false disableForeignKeyConstraintWhenMigrating: true
|
2.2 编辑结构体
新增./config/mysql.go
文件:
package config
import "time"
type mysql struct { Host string `yaml:"host"` Port string `yaml:"port"` User string `yaml:"user"` Password string `yaml:"password"` Database string `yaml:"database"` Charset string `yaml:"charset"` AutoMigrate bool `yaml:"autoMigrate"` ParseTime bool `yaml:"parseTime"` TimeZone string `yaml:"timeZone"` DefaultStringSize uint `yaml:"defaultStringSize"` DisableDatetimePrecision bool `yaml:"disableDatetimePrecision"` SkipInitializeWithVersion bool `yaml:"skipInitializeWithVersion"` Gorm gorm `yaml:"gorm"` SlowSql time.Duration `yaml:"slowSql"` LogLevel string `yaml:"logLevel"` IgnoreRecordNotFoundError bool `yaml:"ignoreRecordNotFoundError"` }
type gorm struct { SkipDefaultTx bool `yaml:"skipDefaultTx"` CoverLogger bool `yaml:"coverLogger"` PreparedStmt bool `yaml:"prepareStmt"` CloseForeignKey bool `yaml:"disableForeignKeyConstraintWhenMigrating"` TablePrefix string `yaml:"tablePrefix"` SingularTable bool `yaml:"singularTable"` }
|
2.3 嵌入主配置
编辑文件:./config/app.go
...
type ServerConfig struct { .... Mysql mysql `yaml:"mysql"` }
|
2.4 定义全局变量
编辑文件:./global/global.go
var ( ... GvaMysqlClient *gorm.DB )
|
3.代码实现
3.1 集成入口
1. 编辑 main.go
func init() { ... initialize.InitGorm() } func main() { ... }
|
2. initialize.InitGorm()
新增文件:./initialize/gorm.go
func InitGorm() { mysqlConfig := global.GvaConfig.Mysql dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=%t&loc=%s", mysqlConfig.User, mysqlConfig.Password, mysqlConfig.Host, mysqlConfig.Port, mysqlConfig.Database, mysqlConfig.Charset, mysqlConfig.ParseTime, mysqlConfig.TimeZone) gormConfig := &gorm.Config{ SkipDefaultTransaction: mysqlConfig.Gorm.SkipDefaultTx, NamingStrategy: schema.NamingStrategy{ TablePrefix: mysqlConfig.Gorm.TablePrefix, SingularTable: mysqlConfig.Gorm.SingularTable, }, PrepareStmt: mysqlConfig.Gorm.PreparedStmt, DisableForeignKeyConstraintWhenMigrating: mysqlConfig.Gorm.CloseForeignKey, } if mysqlConfig.Gorm.CoverLogger { setNewLogger(gormConfig) } client, err := gorm.Open(mysql.New(mysql.Config{ DSN: dsn, DefaultStringSize: mysqlConfig.DefaultStringSize, DisableDatetimePrecision: mysqlConfig.DisableDatetimePrecision, SkipInitializeWithVersion: mysqlConfig.SkipInitializeWithVersion, }), gormConfig) if err != nil { panic(fmt.Sprintf("创建mysql客户端失败: %s", err)) } global.GvaMysqlClient = client if mysqlConfig.AutoMigrate { core.AutoMigrate() } }
|
3.2 重写默认Logger
Gorm
有一个 默认 logger 实现,默认情况下,它会打印慢 SQL
和错误到控制台,也可以重写覆盖,实现写入到单独文件。
编辑文件:./initialize/gorm.go
func setNewLogger(gConfig *gorm.Config) { logPath := global.GvaConfig.Log.Path file, _ := os.OpenFile(logPath+"/sql.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) logLevelMap := map[string]logger.LogLevel{ "error": logger.Error, "info": logger.Info, "warn": logger.Warn, } var logLevel logger.LogLevel var ok bool if logLevel, ok = logLevelMap[global.GvaConfig.Mysql.LogLevel]; !ok { logLevel = logger.Error } newLogger := logger.New(log.New(file, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: global.GvaConfig.Mysql.SlowSql, LogLevel: logLevel, IgnoreRecordNotFoundError: global.GvaConfig.Mysql.IgnoreRecordNotFoundError, Colorful: false, }) gConfig.Logger = newLogger }
|
3.3 数据迁移AutoMigrate
1. 新增实体
新增文件:./model/entity/user.go
package entity
import ( "52lu/go-import-template/global" )
type User struct { global.BaseModel NickName string `json:"nickName" gorm:"type:varchar(20);not null;default:'';comment:昵称"` Phone string `json:"phone" gorm:"type:char(11);unique:un_phone;comment:手机号"` Password string `json:"password" gorm:"type:varchar(20);comment:密码"` Status int `json:"status" gorm:"size:4;default:1;comment:状态 1:正常 2:白名单 3:黑名单"` UserInfo UserInfo `json:"userInfo" gorm:"-"` }
type UserInfo struct { global.BaseModel Uid uint `json:"uid" gorm:"comment:用户id"` Birthday string `json:"birthday" gorm:"type:varchar(10);comment:生日"` Address string `json:"address" gorm:"type:text;comment:地址"` }
|
2. 迁移代码
新增文件:./core/gorm_migrate.go
package core
import ( "52lu/go-import-template/global" "52lu/go-import-template/model/entity" "fmt" "gorm.io/gorm" )
func setTableOption(tableComment string) *gorm.DB { value := fmt.Sprintf("ENGINE=InnoDB COMMENT='%s'", tableComment) return global.GvaMysqlClient.Set("gorm:table_options", value) }
func userTable() { _ = setTableOption("用户表").AutoMigrate(&entity.User{}) _ = setTableOption("用户信息表").AutoMigrate(&entity.UserInfo{}) }
func AutoMigrate() { userTable() }
|
4. 场景示例
下面以登录和注册场景,演示使用和请求流程。
4.1 调用流程
4.2 代码实现
1. 编辑api
新建./api/v1/user_api.go
package v1
import ( "52lu/go-import-template/global" "52lu/go-import-template/model/entity" "52lu/go-import-template/model/request" "52lu/go-import-template/model/response" "52lu/go-import-template/service" "fmt" "github.com/gin-gonic/gin" "go.uber.org/zap" )
func Register(ctx *gin.Context) { var registerParam request.RegisterParam _ = ctx.ShouldBindJSON(®isterParam) register, err := service.Register(registerParam) if err != nil { response.Fail(ctx,"注册失败!") return } response.OkWithData(ctx,register) }
func Login(ctx *gin.Context) { var loginParam request.LoginParam _ = ctx.ShouldBindJSON(&loginParam) fmt.Println("参数:", loginParam) if loginParam.Password == "" || loginParam.Phone == "" { response.Fail(ctx, "手机号和密码不能为空!") return } userRecord := entity.User{Phone: loginParam.Phone, Password: loginParam.Password} if err := service.LoginPwd(&userRecord);err != nil { global.GvaLogger.Error("登录失败:",zap.Any("user",userRecord)) response.Fail(ctx,"登录失败,账号或者密码错误!") return } response.OkWithData(ctx, userRecord) }
|
2. 注册路由
package router import ( v1 "52lu/go-import-template/api/v1" "github.com/gin-gonic/gin" )
func InitUserRouter(engine *gin.Engine) { noLoginGroup := engine.Group("v1/user") { noLoginGroup.POST("login", v1.Login) noLoginGroup.POST("register", v1.Register) } }
|
3.业务处理
新建:./service/user.go
package service
import ( "52lu/go-import-template/global" "52lu/go-import-template/model/entity" "52lu/go-import-template/model/request" "gorm.io/gorm" )
func LoginPwd(user *entity.User) error { result := global.GvaMysqlClient.Where("phone=? and password=?", user.Phone, user.Password). First(user) return result.Error }
func Register(param request.RegisterParam) (*entity.User, error) { user := entity.User{ NickName: param.NickName, Phone: param.Phone, Password: param.Password, } _ = global.GvaMysqlClient.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&user).Error; err != nil { global.GvaLogger.Sugar().Errorf("新增用户失败: %s", err) return err } userInfo := entity.UserInfo{ Uid: user.ID, Birthday: param.Birthday, Address: param.Address, } if err := tx.Create(&userInfo).Error; err != nil { global.GvaLogger.Sugar().Errorf("新增用户信息失败: %s", err) return err } user.UserInfo = userInfo return nil }) return &user, nil }
|