編程基礎 0002_名庫講解

名庫講解

goconfig

go 語言針對 windows 下常見的 ini 格式的配置文件解析器,該解析器在涵蓋了所有 ini 文件操作的基礎上,又針對 go 語言實際開發過程中遇到的一些需求進行了擴展。該解析器最大的優勢在於對注釋的極佳支持,除此之外,支持多個配置文件覆蓋加載也是非常特別但好用的功能。

  • 提供與 windows api 一模一樣的操作
  • 支持遞歸讀取分區
  • 支持自增鍵名
  • 支持對注釋的讀與寫操作
  • 支持直接返回指定類型的鍵值
  • 支持多個文件覆蓋加載

安裝

  • gopm
  • gopm get github.com/Unknwon/goconfig
  • go get
  • go get github.com/Unknwon/goconfig
  • doc

基本使用方法

; conf.ini
; Google
google = www.google.com
search = http://%(google)s

; Here are Comments
; Second line
[Demo]
# This symbol can also make this line to be comments
key1 = Let's us goconfig!!!
key2 = test data
key3 = this is based on key2:%(key2)s
quote = "special case for quote
"key:1" = This is the value of "key:1"
"key:2=key:1" = this is based on "key:2=key:1" => %(key:1)s
中國 = China
chinese-var = hello %(中國)s!
array_key = 1,2,3,4,5
[What's this?]
; Not Enough Comments!!
name = try one more value ^-^
empty_value =

[url]
google_fake = www.google.fake
google_url =  http://%(google_fake)s

[parent]
name = john
relation = father
sex = male
age = 32
money = 1.25

[parent.child]
age = 3
married = true

[parent.child.child]

; Auto increment by setting key to "-"
[auto increment]
- = hello
- = go
- = config
package main

import (
 "github.com/Unknwon/goconfig"
)

// 加載配置文件
cfg,err:=goconfig.LoadConfigFile("conf.ini")
if err!=nil {
    log.Fatalf("無法加載配置文件:%s",err)
}
// 基本讀寫操作
value,err:=cfg.GetValue(goconfig.DEFAULT_SECTION,"key_default")
isInsert:=cfg.SetValue(goconfig.DEFAULT_SECTION,"key_devault","這是新的值")
package main

import (
  "fmt"
  "github.com/Unknwon/goconfig"
  "log"
)

func main() {
  cfg,err := goconfig.LoadConfigFile("conf.ini")
  if err!=nil {
    log.Fatalf("無法加載配置文件:%s",err)
  }
  value,err:=cfg.GetValue("Demo","key1")
  if err!=nil {
    log.Fatalf("無法獲取值{%s}:%s","Demo",err)
  }
  fmt.Printf("%s -> %s : %s","Demo","key1",value)
}
// 注釋 分區注釋 鍵注釋
comment:=cfg.GetSectionComments("super") //獲取分區注釋
log.Printf("分區 %s 的注釋:%s","super",comment)
v:=cfg.SetSectionComments("super","# 這是新的分區注釋") // 井號保留
  • 類型轉換讀取
  • vInt,err:=cfg.Int("must","int") //分區 鍵值
  • Must 系列方法
  • vBool:=cfg.MustBool("must","bool")
  • 刪除指定鍵值
  • ok:=cfg.DeleteKey("must","string")
  • 保存配置文件
  • err=goconfig.SaveConfigFile(cfg,"conf_save.ini")

高級使用

  • 多文件覆蓋加載
  • cfg,err:=goconfig.LoadConfigFile("conf.ini","conf2.ini")
  • err = cfg.AppendFiles("conf3.ini")
  • 配置文件重載
  • err = cfg.Reload()
  • 它會按之前的多個順序來加載
  • 為 must 系統方法設置缺省值
  • vBool:=cfg.MustBool("must","bool404",true)
  • 遞歸讀取鍵值
  • 子分區只設置值不同的相同的不用設置(子分區用.來分隔)
  • 自動鍵名獲取,獲取整個分區 (自增鍵名用-來設置
  • sec,err:=cfg.GetSection("auto increment")
; 鍵值必須是先聲明過的,使用%(鍵名)s
google=www.google.com
search=http://%(goole)s

xorm 基本用法

  • 結構與數據表的雙向映射與增量同步
  • 方法調用鏈式操作
  • session 事務與回滾
  • ORM 方法與純 SQL 混合事務
  • 基於 LRU 規則的緩存器
  • 數據表樂觀鎖
gopm get github.com/go-xorm/xorm
# 或使用go get安裝
go get github.com/go-xorm/xorm

go 語言中的 ORM 習慣直接在定義結構的基礎上配合 tag 來定義模型

type Account struct {
    Id int64
    Name string `xorm:"unique"` // tag
    Balance float64
    Version int `xorm:"version"` //樂觀鎖
}

新建兩個文件main.gomodels.go文件

// main.go
/* 1. 導入數據庫驅動包 這裡以sqlite為例
      a. go get github.com/go-xorm/xorm
      b. go get github.com/mattn/go-sqlite3
      c. 導入驅動包 import(
          _ "github.com/mattn/go-sqlite3" // 因為導入的包必須使用所以要用個佔位,只執行這個包的初始化函數
      )
*/
package main

import (
    "fmt"
    "xorm"
)

const prompt = `Please enter number of operation:
1. Create new account
2. Show detail of account
3. Deposit
4. Withdraw
5. Make transfer
6. List account by Id
7. List account by balance
8. Delete account
9. Exit`

func main() {
    fmt.Println("Welcome bank of xorm!")
Exit:
    for {
        fmt.Println(prompt)
        var num int
        fmt.Scanf("%dn", &num) //回車
        switch num {
        case 1:
            fmt.Println("Place enter <name> <balance>:")
            var name string
            var balance float64
            fmt.Scanf("%s %fn", &name, &balance)
            if err := xorm.NewAccount(name, balance); err != nil {
                fmt.Println(err)
            }
        case 2:
            fmt.Println("Place enter <id>:")
            var id int64
            fmt.Scanf("%dn", &id)
            a, err := xorm.GetAccount(id)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 3:
            fmt.Printf("Please enter <id> <deposit>: ")
            var id int64
            var deposit float64
            fmt.Scanf("%d %fn", &id, &deposit)
            a, err := xorm.MakeDeposit(id, deposit)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 4:
            fmt.Printf("Please enter <id> <withdraw>: ")
            var id int64
            var withdraw float64
            fmt.Scanf("%d %fn", &id, &withdraw)
            a, err := xorm.MakeWithdraw(id, withdraw)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 5:
            fmt.Printf("Please enter <id> <balance> <id>")
            var id1, id2 int64
            var balance float64
            fmt.Scanf("%d %f %d", &id1, &balance, &id2)
            if err := xorm.MackTransfer(id1, id2, balance); err != nil {
                fmt.Println(err)
            }
        case 6:
            as, err := xorm.GetAccountAscId("id")
            if err != nil {
                fmt.Println(err)
            } else {
                for i, a := range as {
                    fmt.Printf("%d %#vn", i, a)
                }
            }
        case 7:
            as, err := xorm.GetAccountAscId("balance")
            if err != nil {
                fmt.Println(err)
            } else {
                for i, a := range as {
                    fmt.Printf("%d %#vn", i, a)
                }
            }
        case 8:
            fmt.Println("Place enter <id>:")
            var id int64
            fmt.Scanf("%dn", &id)
            if err := xorm.DeleteAccount(id);err!=nil {
                fmt.Println(err)
            }
        case 9:
            break Exit

        }
    }
}
// models.go
/*
注意:每個golang源文件中都可以定義一個init函數。golang系統中,所有的源文件都有自己所屬的目錄,每一個目錄都有對應的包名。在包的引用中,一旦某一個包被使用,則這個包下邊的init函數將會被執行,且只執行一次。
1. 新建
_,err:=x.Insert(&Account{Name:name,Balance:balance})
2. 刪除
_,err:=
3. 獲取並修改
a:=&Account{}
has,err:=x.Id(id).Get(a)
a.Balance+=deposit
_,err = x.Update(a)
4. 原始的事務操作 通過version判斷。
*/
package xorm

import (
    "errors"
    "github.com/go-xorm/xorm"
    _ "github.com/mattn/go-sqlite3"
    "log"
)

type Account struct {
    Id      int64  //默認主鍵 如果指定其它為主鍵則要加tag `xorm:"pk"`
    Name    string //`xorm:"unique"` // tag
    Balance float64
    Version int `xorm:"version"` //樂觀鎖
}

var x *xorm.Engine

func init() {
    var err error
    x, err = xorm.NewEngine("sqlite3", "./bank.db") // xorm.NewEngine{"sqlite3","./bank.db"}
    if err != nil {
        log.Fatalf("Fail to create engine %v", err)
    }
    if err = x.Sync(new(Account)); err != nil {
        log.Fatalf("Fal to sync database: %v", err)
    }
}

func NewAccount(name string, balance float64) error {
    //新增記錄
    _, err := x.Insert(&Account{Name: name, Balance: balance})
    return err
}

// 獲取記錄
func GetAccount(id int64) (*Account, error) {
    a := &Account{} //
    has, err := x.Id(id).Get(a)
    if err != nil {
        return nil, err
    } else if !has {
        return nil, errors.New("Account not found")
    }
    return a, nil
}

//存款
func MakeDeposit(id int64, deposit float64) (*Account, error) {
    //a := &Account{}
    a, err := GetAccount(id)
    if err != nil {
        return nil, err
    }
    a.Balance += deposit
    _, err = x.Update(a)
    return a, err
}

//取款
func MakeWithdraw(id int64, withDraw float64) (*Account, error) {
    //a := &Account{}
    a, err := GetAccount(id)
    if err != nil {
        return nil, err
    }
    if a.Balance <= withDraw {
        return nil, errors.New("Not enough balance")
    }
    a.Balance -= withDraw
    _, err = x.Update(a)
    return a, err
}

//轉帳
func MackTransfer(id1, id2 int64, balance float64) error {
    a1, err := GetAccount(id1)
    if err != nil {
        return err
    }
    a2, err := GetAccount(id2)
    if err != nil {
        return err
    }
    if a1.Balance < balance {
        return errors.New("Not enough balance")
    }
    a1.Balance -= balance
    a2.Balance += balance
    if _, err = x.Update(a1); err != nil {
        return err
    } else if _, err = x.Update(a2); err != nil {
        return err
    }
    return nil
}

//批量獲取
/*
  err = x.Desc("balance").Find(&as) //入一個slice的地址
*/
func GetAccountAscId(orderBy string) (as []*Account, err error) {
    err = x.Asc(orderBy).Find(&as)
    return as, err
}

//delete
func DeleteAccount(id int64) error {
    _, err := x.Delete(&Account{Id: id})
    return err
}

xorm 高級用法

事務及回滾

  • 創建 session 對象
  • 啓動事務
  • 通過 session 操作數據庫
  • 發生錯誤時進行回滾
  • 提交事務
// 1.
sess:=x.NewSession()
// 2.
sess.Begin()
// 3.
sess.Update()
// 4.
sess.RollBack()
// 5.
sess.Commit()

修改上例中的轉帳操作

//轉帳
func MackTransfer(id1, id2 int64, balance float64) error {
    a1, err := GetAccount(id1)
    if err != nil {
        return err
    }
    a2, err := GetAccount(id2)
    if err != nil {
        return err
    }
    if a1.Balance < balance {
        return errors.New("Not enough balance")
    }
    a1.Balance -= balance
    a2.Balance += balance

    //if _, err = x.Update(a1); err != nil {
    //    return err
    //} else if _, err = x.Update(a2); err != nil {
    //    return err
    //}
    //return nil
    // 創建session
    sess:=x.NewSession()
    defer sess.Close() //當函數執行完或出錯要釋放資源
    // 開始事務
    if err=sess.Begin();err!=nil {
        return  err
    }
    // 開始事務的更新操作
    if _, err = sess.Update(a1); err != nil {
        sess.Rollback()
        return err
    } else if _, err = sess.Update(a2); err != nil {
        sess.Rollback()
        return err
    }
    // 提交事務
    return sess.Commit()
}

統計記錄條數

x.Count(new(Accout))
// 配合鏈式操作
x.Where("id>10").Count(new(Account))

迭代查詢

  • 迭代查詢某個表中符合條件的所有記錄
  • 使用 Rows 對象
// 迭代查詢
x.Iterate(new(Account),func(idx int,bean interface{}){
    fmt.Printf("%d,%#vn",idx,bean.(*Account))
})
// Rows
func GetRows() {
    a := new(Account)
    rows, err := x.Rows(new(Account))
    if err != nil {
        log.Fatalf("Fail to get row:%vn", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%vn", err)
        }
        fmt.Printf("%#vn", a)
    }
}
// main.go
package main

import (
    "fmt"
    "log"
    "xorm"
)

type Account xorm.Account



func main() {
    fmt.Println("Welcome to bank of xorm2!")
    count, err := xorm.GetNum()
    if err != nil {
        log.Fatalf("Fail to get account count: %vn", err)
    }
    fmt.Println("Account count: ", count)
    for i := count; i < 10; i++ {
        if err = xorm.NewAccount(fmt.Sprintf("joe%d", i), float64(i)*100); err != nil {
            log.Fatalf("Fail to create account %vn", err)
        }
    }
    //    迭代查詢
    fmt.Println("Query all recores:")
    xorm.It()
    fmt.Println("==== Query all recores by Rows:====")
    xorm.GetRows()

}
// models.go /src/xorm/models.go 新增這幾個方法
// Iterator 迭代
func It() {
    x.Iterate(new(Account), printFn)
}

var printFn = func(idx int, bean interface{}) error {
    fmt.Printf("%d:%vn", idx, bean.(*Account))
    return nil
}

func GetRows() {
    a := new(Account)
    rows, err := x.Rows(new(Account))
    if err != nil {
        log.Fatalf("Fail to get row:%vn", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%vn", err)
        }
        fmt.Printf("%#vn", a)
    }
}

常用查詢方法

  • 當只需要結構中的某個字段的值對你有價值時
  • x.Cols("name").Iterate(new(Account),...)
  • 當希望刻意忽略某個字段的查詢結構時
  • x.Omit("name").Iterate(new(Account)...)
  • 查詢結果偏移,這在分頁應用中最為常見
  • x.Limit(3,2).Iterate(new(Account),...)
func GetNameCol()  {
    x.Cols("name").Iterate(new(Account),printFn)
}

func ExcludeCol()  {
    x.Omit("name").Iterate(new(Account),printFn)
}

日誌功能

  • 開啓日誌
  • x.ShowSQL = true
  • 將日誌保存到文件
    f,err:=os.Create("sql.log")
    if err!=nil {
        log.Fatalf("Fail to create log file:%vn",err)
        return
    }
    xorm.NewSimpleLogger(f)
    logger:= xorm.NewSimpleLogger(f)
    logger.ShowSQL(true) // 和教程不一樣,這塊需求注意
    x.SetLogger(logger)

LRU 緩存

設置默認 LRU 緩存

cacher:=xorm.NewLRUCacher(xorm.NewMemoryStore(),1000)
x.SetDefaultCacher(cacher)

事件鈎子

官方提供了 6 類

  • BeforeInsert
  • AfterInsert
  • BeforeUpdate
  • AfterUpdate
  • BeforeDelete
  • AfterDelete
// 作為方法調用
func (a *Account) BeforeInsert(){
    log.Printf("before insert %s", a.Name)
}
func (a *Account) AfterInsert(){
    log.Printf("after insert %s", a.Name)
}

著名案例 Gogs Go Walker GoBuild.IO Sudo China

GoConvery

  • 測試代碼優雅簡潔
  • 直接集成 Go 原生測試
  • 全自動編譯測試
  • 詳細展示測試結構及覆蓋率
  • 高可讀性的命令行輸出結果
  • 半自動化書寫測試

GoConvery 安裝

  • gopm 安裝
  • gopm get github.com/smartystreets/goconvey
  • go get
  • go get github.com/smartystreets/goconvey

通過簡單的示例來實現四則運算的 4 個函數,來使用測試

// main.go 一般不會命名為main包,可做單元測試的包一般都是可做重復利用的包,一般情況下main包不太可能會做重復利用
package goconvey

import "errors"

func Add(a, b int) int {
    return a + b
}
func Substract(a, b int) int {
    return a - b
}

func Multiply(a, b int) int {
    return a * b
}

func Division(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("被除數為0!")
    }
    return a / b, nil
}

編寫測試

//引入兩個包 testing和github.com/smartystreets/goconvey/convey
package goconvey

import (
    . "github.com/smartystreets/goconvey/convey"
    "testing"
)

func TestAdd(t *testing.T) {
    Convey("將兩數相加", t, func() {
        So(Add(1, 2), ShouldEqual, 3)
    })
}

func TestSubstract(t *testing.T) {
    Convey("將兩數相減", t, func() {
        So(Substract(1, 2), ShouldEqual, -1)
    })
}
func TestMultiply(t *testing.T) {
    Convey("將兩數相乘", t, func() {
        So(Multiply(3, 2), ShouldEqual, 6)
    })
}

func TestDivision(t *testing.T) {
    Convey("將兩數相除", t, func() {
        //    嵌套Convery 除0和不除0情況
        //注意嵌套的不用傳t參數
        Convey("被除數為0", func() {
            _, err := Division(10, 0)
            So(err, ShouldNotBeNil)
        })
        Convey("除數不為0", func() {
            num, err := Division(10, 2)
            So(err, ShouldBeNil)
            So(num, ShouldEqual, 5)
        })
    })
}

測試方法有兩種,

一種是使用 go test 如下圖

編程基礎 0002_名庫講解

Convey有幾個就幾個點,也可以使用go test -v 內容會更豐富一下些

另外一種安裝測試覆蓋工具

  • go get code.google.com/p/go.tools/cmd/cover
  • 測試目錄下輸入goconvey 注意 gopath 的設置。

Macaron Web 框架

  • 支持靈活多變的路由規則和組合模式
  • 支持無限路由緩和無限嵌套
  • 支持直接集成現有的服務
  • 支持運行時動態設置需要渲染的模板集
  • 支持對模塊的輕鬆接入與解除
  • 提供便利的依賴注入服務
  • 採用更好的路由層和更少的反射來提升執行速度

  • gopm 安裝

  • gopm get github.com/Unknwon/macaron
  • 通過 go get 安裝
  • go get github.com/Unknwon/macaron

官網

Macaron 框架是從 Gogs 項目中提煉發展而來的,因為在開發 Gogs 的過程中產生了大量輔助模塊可以被重復利用到其它項目中,因此才萌發了創建一個新的框架來提高代碼的利用率的想法

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/6715

(0)
Walker的頭像Walker
上一篇 2026年3月6日 14:30
下一篇 2026年3月6日 13:30

相關推薦

  • Go工程師體系課 004

    需求分析 後台管理系統 商品管理 商品列表 商品分類 品牌管理 品牌分類 訂單管理 訂單列表 用戶信息管理 用戶列表 用戶地址 用戶留言 輪播圖管理 電商系統 登錄頁面 首頁 商品搜索 商品分類導航 輪播圖展示 推薦商品展示 商品詳情頁 商品圖片展示 商品描述 商品規格選擇 加入購物車 購物車 商品列表 數量調整 刪除商品 結算功能 用戶中心 訂單中心 我的…

    2026年3月7日
    10800
  • Go資深工程師講解(慕課) 006_函數式編程

    Go 函數式編程 對應視頻 Ch6(6-2 函數式編程例一),在 002.md 基礎上擴展更多函數式編程模式 1. 回顧:Go 中函數是一等公民 Go 不是純函數式語言,但函數可以作為:- 變量- 參數- 返回值- 存放在數據結構中 // 函數作為變量 var add = func(a, b int) int { return a + b } // 函數作為…

    後端開發 2026年3月6日
    10000
  • Go資深工程師講解(慕課) 007_godoc與代碼生成

    Go 文檔生成與示例代碼 對應視頻 8-6 生成文檔和示例代碼 1. godoc 文檔生成 Go 的文檔直接從源碼注釋中提取,不需要特殊標記語法。 1.1 注釋規範 // Package queue 實現了一個簡單的 FIFO 隊列。 // // 該隊列基於切片實現,支持 Push、Pop 和 IsEmpty 操作。 package queue // Que…

    後端開發 2026年3月6日
    11400
  • 日積月累 電子書目錄與推薦

    Go 語言電子書分類目錄與推薦 電子書來源目錄:/Users/walker/Downloads/www.zxit8.com_017—電子書/共計 48 份資料(47 個編號 PDF + 1 本獨立命名 Go語言實戰 + 1 份源碼 zip)整理時間:2026-03-06 本目錄對全部電子書按主題分類並給出推薦度評級,幫助讀者根據自身水平和需求選擇合適的學習材…

    後端開發 2026年3月7日
    11000
  • Go工程師體系課 015

    Docker 容器化 —— Go 項目實戰指南 一、Docker 核心概念 1.1 甚麼是 Docker Docker 是一個開源的容器化平台,它可以將應用程序及其所有依賴項打包到一個標準化的單元(容器)中,從而實現"一次構建,到處運行"。對於 Go 開發者而言,Docker 解決了以下痛點: 開發環境與生產環境不一致 依賴管理複雜(數據庫、緩存、消息隊列等…

    後端開發 2026年3月7日
    13000
簡體中文 繁體中文 English