编程基础 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月8日 15:11
下一篇 2026年3月9日 12:56

相关推荐

  • 编程基础 0001_基础教程

    go 什么是 Go是一门并发支持、垃圾加收的编译型系统编程语言,具有静态编译语言的高性能和动态语言的,主要特点如下 类型安全和内存安全 以非常直观和极低代价的方案实现高并发 高效的垃圾回收机制 快速编译(同时解决了 C 语言中头文件太多的问题) UTF-8 支持 安装 源码安装 标准包安装 第三方安装 标准包安装,一路下一步。安装完后,会自动添加如下环境变量…

    后端开发 2026年3月6日
    6200
  • Go资深工程师讲解(慕课) 000_课程目录索引

    Google资深工程师深度讲解Go语言 - 课程目录索引 课程来源:慕课网(百度网盘备份)讲师风格:从 Google 工程实践出发,注重底层原理和工程规范 完整视频章节与笔记对照表 章节 视频文件 笔记位置 状态 Ch1 课程介绍 1-1 课程导读 — 跳过 1-2 安装与环境 001.md > GOPATH、环境变量 已覆盖 Ch2 基础语法 2-1…

    后端开发 2026年3月6日
    5600
  • 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) // …

    后端开发 2026年3月6日
    5500
  • Go工程师体系课 004

    需求分析 后台管理系统 商品管理 商品列表 商品分类 品牌管理 品牌分类 订单管理 订单列表 用户信息管理 用户列表 用户地址 用户留言 轮播图管理 电商系统 登录页面 首页 商品搜索 商品分类导航 轮播图展示 推荐商品展示 商品详情页 商品图片展示 商品描述 商品规格选择 加入购物车 购物车 商品列表 数量调整 删除商品 结算功能 用户中心 订单中心 我的…

    2026年3月7日
    5400
  • Go工程师体系课 011

    查询的倒排索引 1. 什么是倒排索引? 倒排索引(Inverted Index)是一种数据结构,用于快速查找包含特定词汇的文档。它是搜索引擎的核心技术之一。 1.1 基本概念 正排索引:文档 ID → 文档内容(词列表) 倒排索引:词 → 包含该词的文档 ID 列表 1.2 为什么叫"倒排"? 倒排索引将传统的"文档包含哪些词"的关系倒转为"词出现在哪些文档…

    后端开发 2026年3月6日
    6600
简体中文 繁体中文 English