编程基础 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
上一篇 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