记录第一次用viper管理配置文件以及用porm进行查询操作| 青训营

189 阅读4分钟

今天是开始做大项目dao层的日子,然后联想到之前学Javaweb有application.yml配置MySQL,后面查阅网上有用viper管理yaml包配置文件的做法,类似项目也有用到相同的构造,就记记笔记学习别人做法方便以后温故知新。

1.安装

go get github.com/spf13/viper

接下来要解释初始化流程和一次简单的查询操作

2.初始化Config流程

2.1viper的读取方法

// Read  ReadConfig
func Read(configName string, configPath string, configType string) *viper.Viper {
    v := viper.New()
    v.SetConfigName(configName) //配置类的前缀名,如config.xxx的config就是前缀名
    v.AddConfigPath(configPath) //配置类的路径,一般放在工作目录下面,之后会用到os包的os.Getwd()方法返回go-project的路径
    v.SetConfigType(configType) //配置类的后缀名(类型),如config.yaml的yaml就是后缀名
    err := v.ReadInConfig()     //这个方法我查询viper的解释是遍历到有这个文件然后读取,底层有用到类似于os.open和os.read()方法
    if err != nil {
        panic(fmt.Errorf("Fatal error config file: %s \n", err)) //路径错误,查询文件失败
    }
    return v
}

2.2config的初始化(会调用上面的Read方法)

首先是config.yaml配置文件的定义(里面暂时主要是mysql的定义):

mysql:
  path: 127.0.0.1
  port: 3306
  config: charset=utf8&parseTime=True&loc=Local
  db-name: douyin_simple
  username: root
  password: asdasd123
  max-idle-conns: 10
  max-open-conns: 100

后面也可以继续添加redis之类一级二级这样向下衍生的配置,然后是初始化方法:

// Config InitConfig
func Config() {
    path, err := os.Getwd() //返回项目目录,具体来说是返回与当前目录对应的根目录名
    if err != nil {
        panic(err)
    }
    v := Read("config", path, "yaml") //在项目目录底下查找config.yaml配置文件
    
    if err := v.Unmarshal(&global.CONFIG); err != nil { //反序列化把yaml的键值对变为结构
        panic(err)
    }
    

注意: &global.CONFIG这里是调用了config文件夹的两个文件config.go和mysql.go(由全局变量global里的CONFIG调用,以后还有Redis这样类似的定义)采用 ' "mapstructure:"xxxx" yaml:"xxxx" ' 将yaml的数据和结构体的属性一一对应。根据我的考查Redis好像也是使用mapstructure库放在config里面解析服务器地址和密码,下面贴出示例定义:

package global
​
import (
    "github.com/life-studied/douyin-simple/config"
    "gorm.io/gorm"
)
​
var (
    DB     *gorm.DB //初始化mysql连接和dao层操作使用
    CONFIG config.Server //初始化配置包
    //以后还有Redis和oss等这样的全局变量
)
​
package config
​
type Server struct {
    Mysql Mysql `mapstructure:"mysql" yaml:"mysql"`
    //接下来Redis这些也在这相似地定义
}
package config
​
type Mysql struct {
    Path         string `mapstructure:"path" yaml:"path"`                     // 服务器地址
    Port         string `mapstructure:"port" yaml:"port"`                     // 端口
    Config       string `mapstructure:"config" yaml:"config"`                 // 高级配置
    Dbname       string `mapstructure:"db-name" yaml:"db-name"`               // 数据库名
    Username     string `mapstructure:"username" yaml:"username"`             // 数据库用户名
    Password     string `mapstructure:"password" yaml:"password"`             // 数据库密码
    MaxIdleConns int    `mapstructure:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
    MaxOpenConns int    `mapstructure:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
}
​
func (m *Mysql) Dsn() string {
    return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config
}
//这里是直接重写一个Dsn()方法,把用户名密码地址这些先拼接起来,直接传给接下来gorm的open()函数比对,DSN=data source name

这里我选择把两个结构体分开写在两个文件中,这样感觉不太冗杂。

4.初始化sql流程

初始化sql顾名思义就是连接mysql使dao层的操作能够进行,这里要用到上文viper获取到的mysql的配置信息(比如连接池大小)通过gorm进行连接配置。

代码如下所示:

package initialize
​
import (
    "github.com/life-studied/douyin-simple/global"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)
​
func Mysql() {
​
    mysqlConfig := mysql.Config{
        DSN:                       global.CONFIG.Mysql.Dsn(), // DSN data source name
        SkipInitializeWithVersion: false,                     // 根据版本自动配置
    }
    var err error
    global.DB, err = gorm.Open(mysql.New(mysqlConfig),
        &gorm.Config{
            PrepareStmt:            true,
            SkipDefaultTransaction: true,
            Logger:                 logger.Default.LogMode(logger.Info),
        },
    ) //启动MySQL,启用预编译SQL语句,不会自动开启事务等功能
    if err != nil {
        panic(err)
    } else {
        sqlDB, _ := global.DB.DB()
        sqlDB.SetMaxIdleConns(global.CONFIG.Mysql.MaxIdleConns) // 设置空闲连接池中连接的最大数量
        sqlDB.SetMaxOpenConns(global.CONFIG.Mysql.MaxOpenConns) // 设置打开数据库连接的最大数量
    }
}
​

5.测试

之后进行模拟的增删改查,例如构建一个测试类:

type Product struct {  
gorm.Model  
Code string //编号  
Price uint //价格  
}

之前需要创建连接的实例来进行方法的调用,如:

func main{
db, err := gorm.Open("mysql", "username:password@/test?charset=utf8&parseTime=True&loc=Local")  
if err != nil {  
panic("failed to connect database")  
}  
defer db.Close()
// 增  
db.Create(&Product{Code: "L1212", Price: 1000}) //增加一个编号为L1212价格为1000的商品
//...以下操作省略
}

通过初始化过后的全局变量DB可以直接使用原来变量的方法查询即可,即:

func main(){
//自动检查 Product 结构是否变化,变化则进行迁移  
global.DB.AutoMigrate(&Product{})  
  
// 增  
global.DB.Create(&Product{Code: "L1212", Price: 1000})  
  
// 查  
var product Product  
global.DB.First(&product, 1) // 找到id为1的产品  
global.DB.Find(&product, "code = ?", "L1212") // 找出 code 为 l1212 的产品  
  
// 改 - 更新产品的价格为 2000  
global.DB.Model(&product).Update("Price", 2000)  
  
// 删 - 删除产品  
global.DB.Delete(&product)
}