数据库操纵
操纵mysql
创建go_learn数据库后创建user表
- CREATE TABLE `user` (
- `user_id` int(11) NOT NULL AUTO_INCREMENT,
- `username` varchar(255) DEFAULT NULL,
- `sex` varchar(255) DEFAULT NULL,
- `email` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`user_id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
复制代码 mysql的前置知识,我们这里就不讲了,可自行去学习mysql教程
Insert
起首,必要引入mysql驱动
通过go get github.com/go-sql-driver/mysql@v1.6.0引入依赖
- _ "github.com/go-sql-driver/mysql"
复制代码 我们的数据库地点是192.168.101.68:3306
用户名:root
暗码:mysql
插入一条纪录:名字bblb,性别man,邮箱bblb123456789@qq.com
- package mainimport ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql"
- "log" "time")var DB *sql.DBfunc init() { db, err := sql.Open("mysql", "root:mysql@tcp(192.168.101.68:3306)/go_learn") if err != nil { panic(err) } //最大空闲毗连数,默认不设置,是2个最大空闲毗连 db.SetMaxIdleConns(5) //最大毗连数,默认不设置,是不限定最大毗连数 db.SetMaxOpenConns(100) // 毗连最大存活时间 db.SetConnMaxLifetime(time.Minute * 3) //空闲毗连最大存活时间 db.SetConnMaxIdleTime(time.Minute * 1) err = db.Ping() if err != nil { log.Println("数据库毗连失败") db.Close() panic(err) } DB = db}func save() { r, err := DB.Exec("insert into user (username,sex,email) values(?,?,?)", "bblb", "man", "bblb123456789@qq.com") if err != nil { log.Println("实行sql语句堕落") panic(err) } id, err := r.LastInsertId() if err != nil { panic(err) } fmt.Println("插入乐成:", id)}func main() { defer DB.Close() save()}
复制代码 检察数据库
Select
- type User struct {
- UserId int `db:"user_id"`
- Username string `db:"username"`
- Sex string `db:"sex"`
- Email string `db:"email"`
- }
- func query(id int) (*User,error) {
- rows, err := DB.Query("select * from user where user_id=? limit 1", id)
- if err != nil{
- log.Println("查询出现错误:",err)
- return nil,errors.New(err.Error())
- }
- user := new(User)
- for rows.Next() {
- if err := rows.Scan(&user.UserId,&user.Username,&user.Sex,&user.Email); err != nil{
- log.Println("scan error:",err)
- return nil,errors.New(err.Error())
- }
- }
- return user,nil
- }
- func main() {
- defer DB.Close()
- //save()
- user,err := query(2)
- if err != nil{
- log.Println("查询出现错误:",err)
- return
- }
- fmt.Printf("查询成功:%+v\n",user)
- }
复制代码 可以看到我们刚刚插入的id是2以是查2
- 查询成功:&{UserId:2 Username:bblb Sex:man Email:bblb123456789@qq.com}
复制代码 Update
- func update(username string, id int) {
- ret, err := DB.Exec("update user set username=? where user_id=?", username, id)
- if err != nil {
- log.Println("更新出现问题:",err)
- return
- }
- affected, _ := ret.RowsAffected()
- fmt.Println("更新成功的行数:",affected)
- }
复制代码 Delete
- func delete(id int) {
- ret, err := DB.Exec("delete from user where user_id=?", id)
- if err != nil {
- log.Println("删除出现问题:",err)
- return
- }
- affected, _ := ret.RowsAffected()
- fmt.Println("删除成功的行数:",affected)
- }
复制代码 sql变乱
mysql变乱特性:
使用tx来举行数据库的变乱操纵。
- func insertTx(username string) {
- tx, err := DB.Begin()
- if err != nil {
- log.Println("开启事务错误:",err)
- return
- }
- ret, err := tx.Exec("insert into user (username,sex,email) values (?,?,?)", username, "man", "test@test.com")
- if err != nil {
- log.Println("事务sql执行出错:",err)
- return
- }
- id, _ := ret.LastInsertId()
- fmt.Println("插入成功:",id)
- if username == "lisi" {
- fmt.Println("回滚...")
- _ = tx.Rollback()
- }else {
- _ = tx.Commit()
- }
- }
复制代码 go操纵Redis
redis不另行先容,默认会,如果不相识,先去学习redis教程
安装:go get github.com/go-redis/redis/v8
- package main
- import (
- "context"
- "fmt"
- "github.com/go-redis/redis/v8"
- )
- func main() {
- ctx := context.Background()
- rdb := redis.NewClient(&redis.Options{
- Addr: "192.168.101.68:6379",
- Password: "redis", // no password set
- DB: 0, // use default DB
- })
- err := rdb.Set(ctx, "key", "value", 0).Err()
- if err != nil {
- panic(err)
- }
- val, err := rdb.Get(ctx, "key").Result()
- if err != nil {
- panic(err)
- }
- fmt.Println("key", val)
- val2, err := rdb.Get(ctx, "key2").Result()
- if err == redis.Nil {
- fmt.Println("key2 does not exist")
- } else if err != nil {
- panic(err)
- } else {
- fmt.Println("key2", val2)
- }
- }
复制代码
泛型
非泛型函数
两个 差异范例的映射:一种用于存储值,一种用于存储值。int64和float64
- package main
- import "fmt"
- // SumInts adds together the values of m.
- func SumInts(m map[string]int64) int64 {
- var s int64
- for _, v := range m {
- s += v
- }
- return s
- }
- // SumFloats adds together the values of m.
- func SumFloats(m map[string]float64) float64 {
- var s float64
- for _, v := range m {
- s += v
- }
- return s
- }
- func main() {
- // Initialize a map for the integer values
- ints := map[string]int64{
- "first": 34,
- "second": 12,
- }
- // Initialize a map for the float values
- floats := map[string]float64{
- "first": 35.98,
- "second": 26.99,
- }
- fmt.Printf("Non-Generic Sums: %v and %v\n",
- SumInts(ints),
- SumFloats(floats))
- }
复制代码 针对差异的范例我们都必要写对应的函数来举行求和,这黑白常贫苦的
泛型函数
Go 1.18 引入了泛型(Generics),这是 Go 语言的一项巨大更新。通过泛型,Go 开发者可以编写更通用、更可复用的代码,而不必要手动编写多个范例的重复代码。
为了支持这一点,将编写一个函数,在添加到其寻常函数参数中。这些范例参数使 function generic,使其可以或许处置惩罚差异范例的参数。将使用范例参数宁静常函数参数调用函数。
- package main
- import "fmt"
- // 定义一个泛型函数,接受一个类型参数 T
- func Print[T any](value T) {
- fmt.Println(value)
- }
- func main() {
- Print(123) // 输出: 123
- Print("hello") // 输出: hello
- }
复制代码 T 是范例参数,any 是 Go 1.18 中的范例束缚,表现可以是任何范例。
在函数 Print[T any](value T) 中,T 是范例参数,表现 value 参数的范例。
可以为范例参数指定束缚,使其只能是某些特定的范例。比方,限定范例参数只能是整数范例:
- package main
- import "fmt"
- // 定义一个泛型函数,限制 T 类型为整型(int, int32, int64)
- func Sum[T int | int32 | int64](a, b T) T {
- return a + b
- }
- func main() {
- fmt.Println(Sum(1, 2)) // 输出: 3
- fmt.Println(Sum(int32(3), int32(4))) // 输出: 7
- }
复制代码 泛型范例
泛型布局体
泛型不但可以用于函数,也可以用于布局体和接口。
- package main
- import "fmt"
- // 定义一个泛型结构体,类型参数 T
- type Pair[T any] struct {
- First T
- Second T
- }
- func main() {
- // 使用泛型结构体,传入 int 类型
- pair1 := Pair[int]{First: 1, Second: 2}
- fmt.Println(pair1) // 输出: {1 2}
- // 使用泛型结构体,传入 string 类型
- pair2 := Pair[string]{First: "hello", Second: "world"}
- fmt.Println(pair2) // 输出: {hello world}
- }
复制代码 在这个例子中,Pair[T any] 是一个泛型布局体,T 代表布局体字段的范例。
泛型接口
- package main
- import "fmt"
- // 定义一个泛型接口,支持多种数值类型(int, float64)
- type Adder[T int | float64] interface {
- Add(a, b T) T
- }
- // 泛型类型:支持任意数值类型
- type NumberAdder[T int | float64] struct{}
- func (na NumberAdder[T]) Add(a, b T) T {
- return a + b
- }
- func main() {
- // 使用 NumberAdder 支持 int 类型
- intAdder := NumberAdder[int]{}
- fmt.Println(intAdder.Add(3, 4)) // 输出: 7
- // 使用 NumberAdder 支持 float64 类型
- floatAdder := NumberAdder[float64]{}
- fmt.Println(floatAdder.Add(3.0, 4.0)) // 输出: 7.0
- }
复制代码 在这里,Adder[T any] 是一个泛型接口,NumberAdder 实现了这个接口。
泛型束缚
泛型支持范例束缚,用于指定范例参数的合法范例范围。范例束缚通过 interface{} 或更具体的接口来实现。
Go 1.18 提供了一些内置的范例束缚,如 any(表现任何范例)和 comparable(表现可以举行比力的范例)。
- package main
- import "fmt"
- // 定义一个泛型函数,约束 T 为可比较类型
- func Compare[T comparable](a, b T) bool {
- return a == b
- }
- func main() {
- fmt.Println(Compare(1, 1)) // 输出: true
- fmt.Println(Compare("a", "b")) // 输出: false
- // fmt.Println(Compare([]int{1}, []int{1})) // 编译错误: slices are not comparable
- }
复制代码 在这个例子中,T comparable 限定了范例参数 T 必须是可以举行比力的范例。
泛型切片和映射
泛型在 Go 中也支持切片(slices)和映射(maps)等常见数据布局。
2.5.1 泛型切片
- package main
- import "fmt"
- func PrintSlice[T any](s []T) {
- for _, v := range s {
- fmt.Println(v)
- }
- }
- func main() {
- PrintSlice([]int{1, 2, 3}) // 输出: 1 2 3
- PrintSlice([]string{"a", "b"}) // 输出: a b
- }
复制代码 泛型映射
- package main
- import "fmt"
- // 泛型映射,支持任意类型作为键和值
- func PrintMap[K comparable, V any](m map[K]V) {
- for k, v := range m {
- fmt.Println(k, v)
- }
- }
- func main() {
- m1 := map[string]int{"a": 1, "b": 2}
- PrintMap(m1) // 输出: a 1 b 2
- m2 := map[int]string{1: "one", 2: "two"}
- PrintMap(m2) // 输出: 1 one 2 two
- }
复制代码 泛型实际应用
Go 中可以用于实现许多常见的算法和数据布局,如链表、栈、队列等。
- package main
- import "fmt"
- type Stack[T any] struct {
- items []T
- }
- func (s *Stack[T]) Push(item T) {
- s.items = append(s.items, item)
- }
- func (s *Stack[T]) Pop() T {
- if len(s.items) == 0 {
- panic("stack is empty")
- }
- item := s.items[len(s.items)-1]
- s.items = s.items[:len(s.items)-1]
- return item
- }
- func main() {
- stack := &Stack[int]{}
- stack.Push(1)
- stack.Push(2)
- fmt.Println(stack.Pop()) // 输出: 2
- fmt.Println(stack.Pop()) // 输出: 1
- }
复制代码 workspace
go 1.18 引入了 workspace 功能,旨在简化多个模块(module)的管理和开发。workspace 答应你在一个工作空间中同时管理多个 Go 模块,这对于开发大型项目或依赖多个模块时非常有效。
概念
工作空间(workspace)是 Go 1.18 引入的一个新概念,它可以包罗多个 Go 模块。如许,你可以在同一个目次下处置惩罚多个模块(module),而无需通过 $GOPATH 来管理它们。
在 Go 1.18 版本中,你必要通过创建一个名为 go.work 的文件来启用工作空间。这个文件界说了工作空间内的 Go 模块及其路径。
workspace案例
工作空间文件 go.work 用来指定工作空间中包罗的模块。比方,如果你有多个模块在差异的目次中,可以在 go.work 中列出它们的路径。
假设你有两个模块 moduleA 和 moduleB,它们位于差异的文件夹中,你可以在根目次下创建一个 go.work 文件,将这两个模块包罗在内:
- go 1.23
- use (
- ./moduleA
- ./moduleB
- )
复制代码 这将指示 Go 使用 moduleA 和 moduleB 两个模块举行构建。
一旦设置了 go.work 文件,Go 下令会自动辨认工作空间并处置惩罚模块之间的依赖关系。比方,你可以使用 go build 或 go run 下令时,Go 会自动处置惩罚跨模块依赖。
比方,运行 go run . 时,Go 会处置惩罚 go.work 文件而且可以跨模块找到所需的依赖,而不必要单独实行每个模块的下令。
假设你有以下文件布局:
- /workspace
- go.work
- /moduleA
- go.mod
- main.go
- /moduleB
- go.mod
- main.go
复制代码 moduleA/go.mod 文件:
moduleB/go.mod 文件:
moduleB/utilsB 文件:
- package moduleB
- import "fmt"
- func HelloB() {
- fmt.Println("HelloB")
- }
复制代码 moduleA/main 文件:
- package main
- import "moduleB"
- func main() {
- moduleB.HelloB()
- }
复制代码 接下来处置惩罚依赖,先创建go.work
再将moduleB加入
Go 会处置惩罚 go.work 中列出的模块路径,并根据需求分析、构建或运行全部模块。
在根目次 /workspace 下运行 go run 或其他 Go 下令。
Go 1.18 引入的工作空间功能使得多模块管理变得更为简单,实用于大型项目或必要调和多个模块的开发场景。通过创建 go.work 文件,可以让 Go 项目更好地管理跨模块依赖,同时进步开发服从和可维护性。
暗昧测试
Go 1.18 引入了 暗昧测试(Fuzzing)功能,它是一种自动化的测试技能,用于发现步调中的埋伏缺陷和安全毛病。暗昧测试通过自动天生大量的随机输入数据来测试步调的坚固性,资助开发者发今世码中的非常活动、瓦解、内存走漏等标题。
在 Go 1.18 中,暗昧测试被集成到了标准库中,你可以直接在 Go 中举行暗昧测试,而不必要额外的工具或库。
概念
暗昧测试是一种通过向步调输入大量随机、偶尔义的(大概故意计划的非常的)数据来检测步调埋伏毛病的技能。它紧张用于:
- 发今世码中的边界情况、瓦解、未处置惩罚的非常等。
- 测试步调对非常输入的处置惩罚本领,加强步调的坚固性。
- 通过大量随机的输入数据测试算法、输入验证、错误处置惩罚等方面。
一样寻常测试
创建add.go
- package fuzzDemo
- func Add(a, b int) int {
- return a + b
- }
复制代码 创建测试用例
- package fuzzDemo
- import (
- "testing"
- )
- func TestAdd(f *testing.T) {
- // 预设一些初始的模糊测试用例
- testcases := []struct {
- a, b int
- want int
- }{
- {1, 2, 3},
- {2, 3, 5},
- {3, 4, 7},
- }
- for _, tc := range testcases {
- if got := Add(tc.a, tc.b); got != tc.want {
- f.Errorf("Add(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want)
- }
- }
- }
复制代码 在根目次运行
输出
- PS F:\Code\Golang\TuLing\workPath\fuzzDemo> go test
- PASSok fuzzDemo 0.012s
复制代码 怎样使用暗昧测试
Go 1.18 引入了对暗昧测试的内置支持,答应通过 testing 包来编写暗昧测试函数。暗昧测试的函数以 Fuzz 开头,使用 testing 包中的 Fuzz 范例来界说。
根本步调:
- 创建暗昧测试函数:在测试代码中界说一个暗昧测试函数,函数的参数是一个范例为 testing.F 的对象,代表暗昧测试框架。
- 编写测试逻辑:在暗昧测试函数内部,编写逻辑来处置惩罚暗昧输入并验证输出。
- 运行测试:使用 go test
下令来运行暗昧测试。
示例:
假设我们有一个函数 Add,它简单地将两个整数相加。
- package main
- import "fmt"
- func Add(a, b int) int {
- return a + b
- }
复制代码 我们可以编写一个暗昧测试函数来测试 Add 函数。
界说暗昧测试函数:
- package main
- import (
- "testing"
- )
- func FuzzAdd(f *testing.F) {
- testcases := []struct {
- a, b int
- }{
- {1, 2},
- {2, 3},
- {3, 4},
- }
- for _, tc := range testcases {
- f.Add(tc.a, tc.b)
- }
- f.Fuzz(func(t *testing.T, a, b int) {
- result := Add(a, b)
- if result != a+b {
- t.Errorf("Add(%d, %d) = %d; want %d", a, b, result, a+b)
- }
- })
- }
复制代码 在这个示例中,我们使用 f.Add 来添加初始的输入值,之后通过 f.Fuzz 来天生随机的输入对,并使用 t.Errorf 来陈诉结果是否符合预期。
运行暗昧测试:
你可以通过 go test
来运行暗昧测试。
此下令会开始实行暗昧测试并天生随机的测试输入来实行 Add 函数。Go 会根据天生的输入验证函数活动是否精确。
暗昧测试参数
- f.Add: 用于为暗昧测试提供初始输入。这些输入会在测试过程中作为种子,基于这些种子数据,Go 会天生更多的随机数据。
- f.Fuzz: 用于界说实际的暗昧测试逻辑。这里你可以编写逻辑来处置惩罚暗昧输入并举行断言。
输出
- PS F:\Code\Golang\TuLing\workPath\fuzzDemo> go test
- -fuzz=FuzzAddfuzz: elapsed: 0s, gathering baseline coverage: 0/3 completedfuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 12 workersfuzz: elapsed: 3s, execs: 591773 (197041/sec), new interesting: 0 (total: 3)fuzz: elapsed: 6s, execs: 1188444 (199093/sec), new interesting: 0 (total: 3)fuzz: elapsed: 8s, execs: 1615691 (198583/sec), new interesting: 0 (total: 3)PASSok fuzzDemo 8.178s
复制代码 暗昧测试常见用法
暗昧测试特殊实用于以下几种场景:
- 函数边界情况测试:比方,测试字符串处置惩罚函数是否能精确处置惩罚空字符串、特殊字符、非常长的字符串等。
- 错误处置惩罚验证:验证步调在汲取到不合法输入时是否会瓦解或产生错误。
- 性能和压力测试:通过给定极限的输入来查抄步调在边界条件下的表现。
自界说输入天生
Go 的暗昧测试功能答应开发者对天生的输入举行更细粒度的控制。比方,你可以为暗昧测试提供自界说的输入天生器,来专门生成特定范例的测试数据。
示例:自界说输入天生
- func FuzzCustomInput(f *testing.F) {
- f.Add([]byte("initial input"))
- f.Fuzz(func(t *testing.T, data []byte) {
- if len(data) > 100 {
- t.Errorf("input data too long: %v", data)
- }
- })
- }
复制代码 运行暗昧测试的其他选项
- -fuzztime: 设置运行暗昧测试的时间限定。默认情况下,暗昧测试会不绝运行直得手动制止,可以通过 -fuzztime 参数来控制运行时长。
- go test
- -fuzz=FuzzAdd -fuzztime=30s
复制代码 这将限定暗昧测试的运行时间为 30 秒。
- -fuzzminimize: 实行最小化产生错误的测试用例,如许可以资助开发者快速定位标题。
- go test
- -fuzz=FuzzAdd -fuzzminimize
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|