編程基礎 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("%d\n", &num) //回車
        switch num {
        case 1:
            fmt.Println("Place enter <name> <balance>:")
            var name string
            var balance float64
            fmt.Scanf("%s %f\n", &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("%d\n", &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 %f\n", &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 %f\n", &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 %#v\n", i, a)
                }
            }
        case 7:
            as, err := xorm.GetAccountAscId("balance")
            if err != nil {
                fmt.Println(err)
            } else {
                for i, a := range as {
                    fmt.Printf("%d %#v\n", i, a)
                }
            }
        case 8:
            fmt.Println("Place enter <id>:")
            var id int64
            fmt.Scanf("%d\n", &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,%#v\n",idx,bean.(*Account))
})
// Rows
func GetRows() {
    a := new(Account)
    rows, err := x.Rows(new(Account))
    if err != nil {
        log.Fatalf("Fail to get row:%v\n", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%v\n", err)
        }
        fmt.Printf("%#v\n", 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: %v\n", 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 %v\n", 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:%v\n", 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:%v\n", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%v\n", err)
        }
        fmt.Printf("%#v\n", 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:%v\n",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
上一篇 12小時前
下一篇 2025年11月25日 07:00

相關推薦

  • Go工程師體系課 016

    Kubernetes 入門 —— Go 微服務部署與編排 一、Kubernetes 核心概念 1.1 什麼是 Kubernetes Kubernetes(簡稱 K8s)是 Google 開源的容器編排平臺,用於自動化部署、擴展和管理容器化應用。如果說 Docker 解決了"如何打包和運行單個容器"的問題,那麼 K8s 解決的是"如何管理成百上千個容器"的問題…

  • Go工程師體系課 003

    grpc grpc grpc-go grpc 無縫集成了 protobuf protobuf 習慣用 Json、XML 數據存儲格式的你們,相信大多都沒聽過 Protocol Buffer。 Protocol Buffer 其實是 Google 出品的一種輕量 & 高效的結構化數據存儲格式,性能比 Json、XML 真的強!太!多! protobuf…

    後端開發 12小時前
    100
  • Go工程師體系課 011

    查詢的倒排索引 1. 什麼是倒排索引? 倒排索引(Inverted Index)是一種數據結構,用於快速查找包含特定詞彙的文檔。它是搜索引擎的核心技術之一。 1.1 基本概念 正排索引:文檔 ID → 文檔內容(詞列表) 倒排索引:詞 → 包含該詞的文檔 ID 列表 1.2 爲什麼叫"倒排"? 倒排索引將傳統的"文檔包含哪些詞"的關係倒轉爲"詞出現在哪些文檔…

    後端開發 4小時前
    000
  • Go工程師體系課 011

    查詢的倒排索引 1. 什麼是倒排索引? 倒排索引(Inverted Index)是一種數據結構,用於快速查找包含特定詞彙的文檔。它是搜索引擎的核心技術之一。 1.1 基本概念 正排索引:文檔 ID → 文檔內容(詞列表) 倒排索引:詞 → 包含該詞的文檔 ID 列表 1.2 爲什麼叫"倒排"? 倒排索引將傳統的"文檔包含哪些詞"的關係倒轉爲"詞出現在哪些文檔…

  • Go資深工程師講解(慕課) 004

    004 goroutine package main import ( "fmt" "time" ) func main() { for i:=0;i<10;i++{ go func(i int) { fmt.Printf("Hello from goroutine %d \n",i) // …

    後端開發 23小時前
    100
簡體中文 繁體中文 English