欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫(kù)初始化GORM

 更新時(shí)間:2024年01月31日 11:06:22   作者:生活處處有BUG  
這篇文章主要為大家介紹了從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫(kù)初始化GORM,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

許多框架都會(huì)引入 ORM 模型來(lái)表示模型類(lèi)和數(shù)據(jù)庫(kù)表的映射關(guān)系,這一篇將使用 gorm[1] 作為 ORM 庫(kù),它遵循了 ActiveRecord(模型與數(shù)據(jù)庫(kù)表一一對(duì)應(yīng)) 模式,并且提供了強(qiáng)大的功能,例如模型關(guān)聯(lián)、關(guān)聯(lián)預(yù)加載、數(shù)據(jù)庫(kù)遷移等,更多內(nèi)容查看官方文檔[2]

安裝

go get -u gorm.io/gorm

# GORM 官方支持 sqlite、mysql、postgres、sqlserver
go get -u gorm.io/driver/mysql 

定義配置項(xiàng)

新建 config/database.go 文件,自定義配置項(xiàng)

package config

type Database struct {
    Driver string `mapstructure:"driver" json:"driver" yaml:"driver"`
    Host string `mapstructure:"host" json:"host" yaml:"host"`
    Port int `mapstructure:"port" json:"port" yaml:"port"`
    Database string `mapstructure:"database" json:"database" yaml:"database"`
    UserName string `mapstructure:"username" json:"username" yaml:"username"`
    Password string `mapstructure:"password" json:"password" yaml:"password"`
    Charset string `mapstructure:"charset" json:"charset" yaml:"charset"`
    MaxIdleConns int `mapstructure:"max_idle_conns" json:"max_idle_conns" yaml:"max_idle_conns"`
    MaxOpenConns int `mapstructure:"max_open_conns" json:"max_open_conns" yaml:"max_open_conns"`
    LogMode string `mapstructure:"log_mode" json:"log_mode" yaml:"log_mode"`
    EnableFileLogWriter bool `mapstructure:"enable_file_log_writer" json:"enable_file_log_writer" yaml:"enable_file_log_writer"`
    LogFilename string `mapstructure:"log_filename" json:"log_filename" yaml:"log_filename"`
}

config/config.go 添加 Database 成員屬性

package config

type Configuration struct {
    App App `mapstructure:"app" json:"app" yaml:"app"`
    Log Log `mapstructure:"log" json:"log" yaml:"log"`
    Database Database `mapstructure:"database" json:"database" yaml:"database"`
}

config.yaml 增加對(duì)應(yīng)配置項(xiàng)

database:
  driver: mysql # 數(shù)據(jù)庫(kù)驅(qū)動(dòng)
  host: 127.0.0.1 # 域名
  port: 3306 # 端口號(hào)
  database: go-test # 數(shù)據(jù)庫(kù)名稱(chēng)
  username: root # 用戶(hù)名
  password: root # 密碼
  charset: utf8mb4 # 編碼格式
  max_idle_conns: 10 # 空閑連接池中連接的最大數(shù)量
  max_open_conns: 100 # 打開(kāi)數(shù)據(jù)庫(kù)連接的最大數(shù)量
  log_mode: info # 日志級(jí)別
  enable_file_log_writer: true # 是否啟用日志文件
  log_filename: sql.log # 日志文件名稱(chēng)

自定義 Logger(使用文件記錄日志)

gorm 有一個(gè)默認(rèn)的 logger[3] ,由于日志內(nèi)容是輸出到控制臺(tái)的,我們需要自定義一個(gè)寫(xiě)入器,將默認(rèn)logger.Writer 接口的實(shí)現(xiàn)切換為自定義的寫(xiě)入器,上一篇引入了 lumberjack ,將繼續(xù)使用它。

新建 bootstrap/db.go 文件,編寫(xiě) getGormLogWriter 函數(shù):

package bootstrap
import (
    "gopkg.in/natefinch/lumberjack.v2"
    "gorm.io/gorm/logger"
    "io"
    "jassue-gin/global"
    "log"
    "os"
)
// 自定義 gorm Writer
func getGormLogWriter() logger.Writer {
    var writer io.Writer
    // 是否啟用日志文件
    if global.App.Config.Database.EnableFileLogWriter {
        // 自定義 Writer
        writer = &lumberjack.Logger{
            Filename:   global.App.Config.Log.RootDir + "/" + global.App.Config.Database.LogFilename,
            MaxSize:    global.App.Config.Log.MaxSize,
            MaxBackups: global.App.Config.Log.MaxBackups,
            MaxAge:     global.App.Config.Log.MaxAge,
            Compress:   global.App.Config.Log.Compress,
        }
    } else {
        // 默認(rèn) Writer
        writer = os.Stdout
    }
    return log.New(writer, "\r\n", log.LstdFlags)
}

接下來(lái),編寫(xiě) getGormLogger 函數(shù), 切換默認(rèn) Logger 使用的 Writer

func getGormLogger() logger.Interface {
    var logMode logger.LogLevel
    switch global.App.Config.Database.LogMode {
    case "silent":
        logMode = logger.Silent
    case "error":
        logMode = logger.Error
    case "warn":
        logMode = logger.Warn
    case "info":
        logMode = logger.Info
    default:
        logMode = logger.Info
    }
    return logger.New(getGormLogWriter(), logger.Config{
        SlowThreshold:             200 * time.Millisecond, // 慢 SQL 閾值
        LogLevel:                  logMode, // 日志級(jí)別
        IgnoreRecordNotFoundError: false, // 忽略ErrRecordNotFound(記錄未找到)錯(cuò)誤
        Colorful:                  !global.App.Config.Database.EnableFileLogWriter, // 禁用彩色打印
    })
}

至此,自定義 Logger 就已經(jīng)實(shí)現(xiàn)了,這里只簡(jiǎn)單替換了 logger.Writer 的實(shí)現(xiàn),大家可以根據(jù)各自的需求做其它定制化配置

初始化數(shù)據(jù)庫(kù)

在 bootstrap/db.go 文件中,編寫(xiě) InitializeDB 初始化數(shù)據(jù)庫(kù)函數(shù),以便于在 main.go 中調(diào)用

package bootstrap
import (
    "gopkg.in/natefinch/lumberjack.v2"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "io"
    "jassue-gin/global"
    "log"
    "os"
    "strconv"
    "time"
)
func InitializeDB() *gorm.DB {
    // 根據(jù)驅(qū)動(dòng)配置進(jìn)行初始化
    switch global.App.Config.Database.Driver {
    case "mysql":
       return initMySqlGorm()
    default:
       return initMySqlGorm()
    }
}
// 初始化 mysql gorm.DB
func initMySqlGorm() *gorm.DB {
    dbConfig := global.App.Config.Database
    if dbConfig.Database == "" {
        return nil
    }
    dsn := dbConfig.UserName + ":" + dbConfig.Password + "@tcp(" + dbConfig.Host + ":" + strconv.Itoa(dbConfig.Port) + ")/" +
        dbConfig.Database + "?charset=" + dbConfig.Charset +"&parseTime=True&loc=Local"
    mysqlConfig := mysql.Config{
        DSN:                       dsn,   // DSN data source name
        DefaultStringSize:         191,   // string 類(lèi)型字段的默認(rèn)長(zhǎng)度
        DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的數(shù)據(jù)庫(kù)不支持
        DontSupportRenameIndex:    true,  // 重命名索引時(shí)采用刪除并新建的方式,MySQL 5.7 之前的數(shù)據(jù)庫(kù)和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的數(shù)據(jù)庫(kù)和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false, // 根據(jù)版本自動(dòng)配置
    }
    if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{
        DisableForeignKeyConstraintWhenMigrating: true, // 禁用自動(dòng)創(chuàng)建外鍵約束
        Logger: getGormLogger(), // 使用自定義 Logger
    }); err != nil {
        global.App.Log.Error("mysql connect failed, err:", zap.Any("err", err))
        return nil
    } else {
        sqlDB, _ := db.DB()
        sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns)
        sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns)
        return db
    }
}
func getGormLogger() logger.Interface {
    var logMode logger.LogLevel
    switch global.App.Config.Database.LogMode {
    case "silent":
        logMode = logger.Silent
    case "error":
        logMode = logger.Error
    case "warn":
        logMode = logger.Warn
    case "info":
        logMode = logger.Info
    default:
        logMode = logger.Info
    }
    return logger.New(getGormLogWriter(), logger.Config{
        SlowThreshold:             200 * time.Millisecond, // 慢 SQL 閾值
        LogLevel:                  logMode, // 日志級(jí)別
        IgnoreRecordNotFoundError: false, // 忽略ErrRecordNotFound(記錄未找到)錯(cuò)誤
        Colorful:                  !global.App.Config.Database.EnableFileLogWriter, // 禁用彩色打印
    })
}
// 自定義 gorm Writer
func getGormLogWriter() logger.Writer {
    var writer io.Writer
    // 是否啟用日志文件
    if global.App.Config.Database.EnableFileLogWriter {
        // 自定義 Writer
        writer = &lumberjack.Logger{
            Filename:   global.App.Config.Log.RootDir + "/" + global.App.Config.Database.LogFilename,
            MaxSize:    global.App.Config.Log.MaxSize,
            MaxBackups: global.App.Config.Log.MaxBackups,
            MaxAge:     global.App.Config.Log.MaxAge,
            Compress:   global.App.Config.Log.Compress,
        }
    } else {
        // 默認(rèn) Writer
        writer = os.Stdout
    }
    return log.New(writer, "\r\n", log.LstdFlags)
}

編寫(xiě)模型文件進(jìn)行數(shù)據(jù)庫(kù)遷移

新建 app/models/common.go 文件,定義公用模型字段

package models

import (
    "gorm.io/gorm"
    "time"
)

// 自增ID主鍵
type ID struct {
    ID uint `json:"id" gorm:"primaryKey"`
}

// 創(chuàng)建、更新時(shí)間
type Timestamps struct {
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

// 軟刪除
type SoftDeletes struct {
    DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
}

新建 app/models/user.go 文件,定義 User 模型

package models

type User struct {
    ID
    Name string `json:"name" gorm:"not null;comment:用戶(hù)名稱(chēng)"`
    Mobile string `json:"mobile" gorm:"not null;index;comment:用戶(hù)手機(jī)號(hào)"`
    Password string `json:"password" gorm:"not null;default:'';comment:用戶(hù)密碼"`
    Timestamps
    SoftDeletes
}

在 bootstrap/db.go 文件中,編寫(xiě)數(shù)據(jù)庫(kù)表初始化代碼

func initMySqlGorm() *gorm.DB {
    // ...
    if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{
        DisableForeignKeyConstraintWhenMigrating: true, // 禁用自動(dòng)創(chuàng)建外鍵約束
        Logger: getGormLogger(), // 使用自定義 Logger
    }); err != nil {
        return nil
    } else {
        sqlDB, _ := db.DB()
        sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns)
        sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns)
        initMySqlTables(db)
        return db
    }
}

// 數(shù)據(jù)庫(kù)表初始化
func initMySqlTables(db *gorm.DB) {
    err := db.AutoMigrate(
        models.User{},
    )
    if err != nil {
        global.App.Log.Error("migrate table failed", zap.Any("err", err))
        os.Exit(0)
    }
}

定義全局變量 DB

在 global/app.go 中,編寫(xiě):

package global

import (
    "github.com/spf13/viper"
    "go.uber.org/zap"
    "gorm.io/gorm"
    "jassue-gin/config"
)

type Application struct {
    ConfigViper *viper.Viper
    Config config.Configuration
    Log *zap.Logger
    DB *gorm.DB
}

var App = new(Application)

測(cè)試

在 main.go 中調(diào)用數(shù)據(jù)庫(kù)初始化函數(shù)

package main

import (
    "github.com/gin-gonic/gin"
    "jassue-gin/bootstrap"
    "jassue-gin/global"
    "net/http"
)

func main() {
    // 初始化配置
    bootstrap.InitializeConfig()

    // 初始化日志
    global.App.Log = bootstrap.InitializeLog()
    global.App.Log.Info("log init success!")

    // 初始化數(shù)據(jù)庫(kù)
    global.App.DB = bootstrap.InitializeDB()
    // 程序關(guān)閉前,釋放數(shù)據(jù)庫(kù)連接
    defer func() {
        if global.App.DB != nil {
            db, _ := global.App.DB.DB()
            db.Close()
        }
    }()

    r := gin.Default()

    // 測(cè)試路由
    r.GET("/ping", func(c *gin.Context) {
        c.String(http.StatusOK, "test gorm")
    })

    // 啟動(dòng)服務(wù)器
    r.Run(":" + global.App.Config.App.Port)
}

啟動(dòng) main.go ,由于我還沒(méi)有創(chuàng)建 go-test 數(shù)據(jù)庫(kù),并且調(diào)整了 logger.Writer 為 lumberjack,所以會(huì)生成 storage/logs/sql.log 文件,文件內(nèi)容如下:

2021/10/13 19:17:47 /Users/sujunjie/go/src/jassue-gin/bootstrap/db.go:44
[error] failed to initialize database, got error Error 1049: Unknown database 'go-test'

創(chuàng)建 go-test 數(shù)據(jù)庫(kù),重新啟動(dòng) main.go ,users 表創(chuàng)建成功。

以上就是從零封裝Gin框架實(shí)現(xiàn)數(shù)據(jù)庫(kù)初始化GORM的詳細(xì)內(nèi)容,更多關(guān)于Gin GORM數(shù)據(jù)庫(kù)初始化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang gin框架獲取參數(shù)的操作

    golang gin框架獲取參數(shù)的操作

    這篇文章主要介紹了golang gin框架獲取參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go語(yǔ)言中g(shù)o?mod?vendor使用方法

    Go語(yǔ)言中g(shù)o?mod?vendor使用方法

    go mod vendor的功能是將新增的依賴(lài)包自動(dòng)寫(xiě)入當(dāng)前項(xiàng)目的 vendor目錄,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言中g(shù)o?mod?vendor使用的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • golang中make和new的區(qū)別示例詳解

    golang中make和new的區(qū)別示例詳解

    Go 語(yǔ)言中的 new 和 make 一直是新手比較容易混淆的東西,咋一看很相似。不過(guò)解釋兩者之間的不同也非常容易,下面這篇文章主要介紹了golang中make和new的區(qū)別,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-08-08
  • 詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆

    詳解Golang如何實(shí)現(xiàn)支持隨機(jī)刪除元素的堆

    堆是一種非常常用的數(shù)據(jù)結(jié)構(gòu),它能夠支持在O(1)的時(shí)間復(fù)雜度獲取到最大值(或最小值)。本文主要介紹了如何實(shí)現(xiàn)支持O(log(n))隨機(jī)刪除元素的堆,需要的可以參考一下
    2022-09-09
  • Go語(yǔ)言中處理JSON數(shù)據(jù)的編碼和解碼的方法

    Go語(yǔ)言中處理JSON數(shù)據(jù)的編碼和解碼的方法

    在Go語(yǔ)言中,處理JSON數(shù)據(jù)的編碼和解碼主要依賴(lài)于標(biāo)準(zhǔn)庫(kù)中的encoding/json包,這個(gè)包提供了兩個(gè)核心的函數(shù):Marshal和Unmarshal,本文給大家介紹了Go語(yǔ)言中處理JSON數(shù)據(jù)的編碼和解碼的方法,需要的朋友可以參考下
    2024-04-04
  • Golang中反射的常見(jiàn)用法分享

    Golang中反射的常見(jiàn)用法分享

    本篇文章主要為大家詳細(xì)介紹一些Go語(yǔ)言中常見(jiàn)的反射用法,涵蓋了常見(jiàn)的數(shù)據(jù)類(lèi)型的反射操作。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-01-01
  • 快速升級(jí)Go版本(幾分鐘就搞定了)

    快速升級(jí)Go版本(幾分鐘就搞定了)

    go現(xiàn)在的更新速度是非常的快啊,用著用著網(wǎng)上的教程就不配套了,下面這篇文章主要給大家介紹了關(guān)于快速升級(jí)Go版本的相關(guān)資料,文中介紹的方法幾分鐘就搞定了,需要的朋友可以參考下
    2024-05-05
  • golang編程入門(mén)之http請(qǐng)求天氣實(shí)例

    golang編程入門(mén)之http請(qǐng)求天氣實(shí)例

    這篇文章主要介紹了golang編程入門(mén)之http請(qǐng)求天氣實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • 解析Go語(yǔ)言編程中的struct結(jié)構(gòu)

    解析Go語(yǔ)言編程中的struct結(jié)構(gòu)

    這篇文章主要介紹了Go語(yǔ)言編程中的struct結(jié)構(gòu),是Go語(yǔ)言入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-10-10
  • Go?1.21新內(nèi)置函數(shù)min、max和clear的用法詳解

    Go?1.21新內(nèi)置函數(shù)min、max和clear的用法詳解

    Go?1.21?版本已經(jīng)正式發(fā)布,它帶來(lái)了許多新特性和改進(jìn),其中引入了的三個(gè)新內(nèi)置函數(shù):max、min?和?clear,接下來(lái)我們就來(lái)看看這些函數(shù)的用途和特點(diǎn)吧
    2023-08-08

最新評(píng)論