生活札记

golang学习笔记 - 高阶(三)

copylian    1 评论    10921 浏览    2022.05.17

一、常用命令

        bug             启动bug报告

        build           编译包和依赖项

        clean           删除对象文件和缓存文件

        doc             显示包裹或符号的文档

        env             打印Go环境信息

        fix               更新包以使用新的API

        fmt             gofmt(重新格式化)包源

        generate    按处理源生成生成Go文件

        get             获取将依赖项添加到当前模块并安装它们

        install         编译和安装程序包及依赖项

        list              列出包或模块

        mod           模块维护

        work          工作区维护

        run             编译并运行Go程序

        test            测试包

        tool            运行指定的go程序

        version      Go版本

        vet             报告包装中可能的错误


二、vscode快捷键使用

多行选中:Shift+Alt+↑或↓

删除行:Ctrl+Shift+K

查找文件:Ctrl+E

打开命令行设置:Ctrl+Shift+P

切换项目:Ctrl+Shift+~


快速生成代码片段:

pkgm:main包+main主函数

ff:fmt.Printf("", var)

fp:fmt.Println("")

forr:for _, v := range v {}

fmain:func main() {}

xxx.var!:s := xxx

xxx.print!:fmt.Printf("xxx: %v\n", xxx)

xxx.split!:strings.Split(s, "")


三、高阶函数:函数作为形参或者返回值

//函数作为参数

func Gfun(sex string, name string, age int, f func(string, int)) {

    fmt.Printf("sex: %v\n", sex)


    //形参函数

    f(name, age)

}


//形参函数

func NameAge(name string, age int) {

    fmt.Printf("name: %v\n", name)

    fmt.Printf("age: %v\n", age)

}


//函数作为返回值

func Rfun(a int, b int) func(int, int) int {

    //返回值函数

    return BackFuna

}


//返回函数

func BackFuna(a int, b int) int {

    return a + b

}


//执行函数

func ExecFun() {

    //形参函数

    Gfun("女", "飓风呀", 100, NameAge)


    //返回函数

    s := Rfun(1, 2)

    i := s(1, 2)

    fmt.Printf("i: %v\n", i)

}


四、指针变量、指针数组(表示数组里面的元素的类型是指针类型)、结构体指针

//指针变量、指针数组

func Pointers() {

    //整数指针类型

    var a *int                 //指针类型

    var a1 int                 //普通类型

    fmt.Printf("a: %v\n", a)   //nil

    fmt.Printf("a1: %v\n", a1) //0


    i := 100

    a = &i

    fmt.Printf("a: %T\n", a)  //*int

    fmt.Printf("a: %v\n", *a) //内存地址


    //字符串指针类型

    var s *string

    s1 := "abc"

    s = &s1

    fmt.Printf("s: %v\n", *s) //ab

    fmt.Printf("s: %T\n", s)  //*string


    //指针数组

    var arr = make([]*int, 3)

    arr2 := []int{1, 2, 3}

    fmt.Printf("arr: %v\n", arr)   //[]

    fmt.Printf("arr: %T\n", arr)   //[]*int

    fmt.Printf("arr2: %v\n", arr2) //[1,2,3]


    for i, _ := range arr2 {

        arr[i] = &arr2[i]

    }

    fmt.Printf("arr: %T\n", arr)

    for _, v := range arr {

        fmt.Printf("v: %v\n", *v)

    }


    //结构体指针

    p := Person{

        Name: "abc",

    }


    fmt.Printf("p: %v\n", p) //{abc}

    fmt.Printf("p: %T\n", p) //*notes.Person


    var p1 *Person //结构体指针

    p1 = &p

    fmt.Printf("p1: %v\n", *p1) //{abc}

    fmt.Printf("p1: %T\n", p1) //*notes.Person


    p2 := new(Person)

    p2.Name = "aaa"

    fmt.Printf("p2: %v\n", *p2) //{aaa}

}


五、sync.WaitGroup:同步等待

//sync.WaitGroup

var wg sync.WaitGroup


//方法

func swg(i int) {

    //减1

    wg.Done()

    fmt.Println(i)

}


func main() {

    //sync.WaitGroup

    for i := 0; i < 10; i++ {

        //协程

        go swg(i)


        //加1

        wg.Add(1)

    }


    //等待到0

    wg.Wait()


    //主函数

    fmt.Println("End...")

}


六、runtime包

func RuntimeFun() {

    go PrintGo("Go") //子协程

    runtime.Gosched() //我有权利执行任务了,我让给其他程序执行


    fmt.Println("GONUMCPU:", runtime.NumCPU())

    runtime.GOMAXPROCS(1)

    go PrintGo("Java") //子协程

    go PrintGo("PHP")  //子协程

}


七、atomic包:原子操作,保证数据的原始性

func AtomicFun() {

    //32位、64位

    var i int32 = 100


    //加

    atomic.AddInt32(&i, 1)

    fmt.Printf("i: %v\n", i)


    //减

    atomic.AddInt32(&i, -1)

    fmt.Printf("i: %v\n", i)


    //加载

    a := atomic.LoadInt32(&i)

    fmt.Printf("a: %v\n", a)


    //存储

    atomic.StoreInt32(&i, 200)

    fmt.Printf("i: %v\n", i)


    //对比交换

    atomic.CompareAndSwapInt32(&i, 200, 300)

    fmt.Printf("i: %v\n", i)

}


八、os模块、File操作

1)、文件夹、目录

//创建文件

os.Create("test.txt")


//创建目录

os.MkdirAll("test/a/b", os.ModPerm)


//获取当前目录

os.Getwd()


//进入指定目录

os.Chdir("d:/")


//读入目录

dirs, err := os.ReadDir("a")

for _,v  := range dirs {

    fmt.Printf("v.IsDir():%v\n", v.IsDir())

    fmt.Printf("v.Name():%v\n", v.Name())

}


2)、文件

//文件读取

f, err := os.Open("test.txt")

buf := make([]btye, 4)

f.ReadAt(buf, 3)


//文件写入

f, err := os.Open("test.txt")

f.WriteAt([]byte("abc"), 3)


3)、环境变量

//获取所有环境变量

os.Environ()


//获取某个环境变量

os.Getenv("GOPATH")


//查找环境变量

os.LookupEnv("GOPATH")


//替换、设置环境变量

os.Setenv("NAME", "aaa")

os.Setenv("NAME_PATH", "aaa")


//扩展环境变量

os.ExpandEnv("$NAME live is ${NAME_PATH}")


//清空所有环境变量

os.Clearenv()


4)、io

//复制

io.Copy(os.Stdout, r)


//缓存区copy

io.BufferCopy(os.Stdout, r , buf)


//限制读取

io.LimitReader(r, 4)


//批量读取

io.MultiReader(r1, r2, r3)


//批量写入

io.MultiWriter(&buf1, &buf2)


//读取范围

io.NewSelectionReader(r, 1, 10)


//写入

io.WriteString(os.Stdout, "abc")



九、io(input、output)、ioutil包、bufio包

//读取

r := strings.NewReader("Hello World")

buf := make([]btye, 5)

r.Read(buf)


//读取

ioutil.ReadAll(file)


//读取文件夹

ioutil.ReadDir(".")


//读取文件

ioutil.ReadFile("test.txt")


//写入文件

ioutil.WriteFile("test.txt", []byte("aaa"), 0777)


//临时文件

tempfile, err := ioutil.TempFile("", "abc")

tempfile.Name()

tempfile.Write([]byte("aaa"))

tempfile.Close()


//bufio

s := strings.NewReader("abc")

br := bufio.NewReader(s)

br.ReadByte() //读取一个字节

br.UnreadByte() //回退一个字节

br.ReadRune()  //读取utf-8一个字节

br.UnreadRune()  //回退utf-8一个字节

br.WriteTo(f) //写入


//bufio Scanner:扫描

s := strings.NewReader("abc")

bs := bufio.NewScanner(s)

bs.Split(bufio.ScanWords) //以空格作为分隔符

bs.Split(bufio.ScanBytes) //以字节作为分隔符

bs.Split(bufio.ScanRunes) //以utf-8字节作为分隔符

for _, v := range{

    fmt.Printf("v.Text() = %s\n", v.Text())

}


参考:Go语言的IO库那么多纠结该如何选择

参考:文件读写os、io、bufio、ioutil


十、log日志、builtin内建、bytes字节、json

//log日志

log.Print("a")

log.Printf("a%d", 10)

log.Println("aa", b)


log.Panic() //恐慌

log.Fatal() //致命错误os.Exit()


log.SetFlags(log.Ldate | log.Ltime | log.Shortfile) //设置日志flag

log.SetPrefix("aaa_")  //日志前缀


log.SetOutput(f) //日志写入文件

log.Flags() //获取日志flags


logger := log.New(f, "prefix_", log.Ldate | log.Ltime | log.Shortfile) //直接写入日志

logger.Print("log") //输出日志


//builtin内建

new与make的区别

1、make只能用来分配及初始化slice、map、chan的数据,new可以分配任意的数据

2、new分配返回指针,即类型*T,make返回引用,即T

3、new分配的空间被清零,make分配后,会被初始化


//bytes字节

b := byte.Runes("中文utf-8字符") //utf-8

fmt.Println("len=%d\n", len(b))

byte.Join(b1, b2) //连接


var b bytes.Buffer

b := bytes.NewBufferString("aaa")

b := bytes.NewBuffer([]btye("bbb"))

b.WriteRune([]rune("中文"))


//json.Marshal()、json.Unmarshal()、json.NewDecoder()、json.NewEncoder()

b := []btye(‘{"Name": "a", "Age": 28, "Parents": ["A", "B"]}’))

var f map[string]interface{}

json.Unmarshal(b, &f) //json解析到f


//解析json

f, err := os.Open("a.json")

d := json.NewDecoder(f)

var v map[string]interface{}

d.Decode(&v) //数据写入v切片中


//json写入文件

p := Persen{

    "Name":"aaa",

    "Age":28

}

f, err := os.OpenFile("a.json", os.WRONLY, 0777)

e := json.NewEncoder(f)

e.Encode(p)


十一、Mysql基础操作

下载go get github.com/go-sql-driver/mysql

安装:

    import "database/sql"

    import "github.com/go-sql-driver/mysql"

更新:go mod tidy


//mysql连接

func InitDb() (db *sql.DB, err error) {

    

    //dsn

    dsn := "root:password@tcp(127.0.0.1:3306)/go?charset=utf8mb4&parseTime=True"

    

    //链接数据库:不会校验密码是否正确

    db, err_1 := sql.Open("mysql", dsn)

    if err_1 != nil {

        return db, err_1

    }

    

    //尝试与数据库建立连接(校验dsn是否正确)

    err_2 := db.Ping()

    if err_2 != nil {

        return db, err_2

    }

    

    //设置参数

    db.SetConnMaxLifetime(time.Minute * 3)

    db.SetMaxIdleConns(10)

    db.SetMaxOpenConns(10)

    return db, nil

}


//mysql:插入

func InsertSql() {

    //连接数据库

    db, err := InitDb()

    if err != nil {

        panic(err)

    }

    

    //sql

    sql := "insert into user(name,password,sex,age) values(?,?,?,?)"

    

    //执行

    r, err := db.Exec(sql, "abcd", "aaaa123", 2, 1)

    if err != nil {

        panic(err)

    }

    

    //获取插入最新ID

    id, err1 := r.LastInsertId()

    if err1 != nil {

        panic(err1)

    }

    fmt.Println("LastInsertId:", id)

    

    //关闭数据库

    defer db.Close()

}


type User struct {

    Id       int

    Name     string

    Password string

    Age      int

    Sex      int

}


//mysql:查询

func QueryRowSql() {

    //连接数据库

    db, err := InitDb()

    if err != nil {

        panic(err)

    }

    

    //sql

    sql := "select * from user where id = ?"

    

    //查询单行

    var u User

    err_1 := db.QueryRow(sql, 2).Scan(&u.Id, &u.Name, &u.Password, &u.Age, &u.Sex)

    if err_1 != nil {

        panic(err_1)

    }

    fmt.Println(u)

    

    //查询多行

    //sql

    sql_1 := "select * from user"

    r, err := db.Query(sql_1) //查询

    if err != nil {

        panic(err)

    }

    

    //处理数据

    for r.Next() {

        //数据扫描到结构体

        r.Scan(&u.Id, &u.Name, &u.Password, &u.Age, &u.Sex)

        fmt.Println(u)

    }

    

    //关闭查询

    r.Close()

}


//mysql:更新

func UpdateSql() {

    //连接数据库

    db, err := InitDb()

    if err != nil {

        panic(err)

    }

    

    //sql

    sql := "update user set name=?,password=? where id = ?"

    r, err := db.Exec(sql, 5, 1, 1)

    if err != nil {

        panic(err)

    }

    

    //影响行数

    n, err := r.RowsAffected()

    if err != nil {

        panic(err)

    }

    fmt.Println(n)

}


//mysql:删除

func DeleteSql() {

    //连接数据库

    db, err := InitDb()

    if err != nil {

        panic(err)

    }

    

    //sql

    sql := "delete from user where id = ?"

    r, err := db.Exec(sql, 3)

    if err != nil {

        panic(err)

    }

    

    //影响行数

    n, err := r.RowsAffected()

    if err != nil {

        panic(err)

    }

    fmt.Println(n)

}


十二、GORM:对象关系映射

教程:https://www.bilibili.com/video/BV1zR4y1t7Wj?p=112

笔记:https://golang-tech-stack.com/tutorial/gorm

官网:https://gorm.io/

文档:https://gorm.io/zh_CN/docs/

下载:

    go get -u gorm.io/gorm

    go get -u gorm.io/driver/sqlite

安装:

    import "gorm.io/gorm"

    import "gorm.io/driver/mysql"

更新:go mod tidy


1)、基础操作

package gormtest


import (

    "fmt"

    "gorm.io/driver/mysql"

    "gorm.io/gorm"

)


type Product struct {

    gorm.Model //继承

    Code       string

    Prize      uint

}


//公共DB

var db *gorm.DB


//自动执行函数

func init() {

    //连接gorm

    InitGorm()

}


//连接gorm

func InitGorm() {

    //dsn连接

    dsn := "root:password@tcp(127.0.0.1:3306)/go?charset=utf8mb4&parseTime=True&loc=Local"

    // mysql.New(mysql.Config{

    // DSN: dsn,

    // // DefaultStringSize             uint

    // // DefaultDatetimePrecision      *int

    // // DisableDatetimePrecision      bool

    // // DontSupportRenameIndex        bool

    // // DontSupportRenameColumn       bool

    // // DontSupportForShareClause     bool

    // // DontSupportNullAsDefaultValue bool

    // })

    d, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

    if err != nil {

        panic(err)

    }

    db = d

}


//创建表

func Create() {


    //实例化

    var p Product

    

    //创建表

    err := db.AutoMigrate(&p)

    if err != nil {

        panic(err)

    }

    fmt.Println("创建表成功")

}


//创建前置钩子

func (p *Product) BeforeCreate(tx *gorm.DB) (err error) {

    fmt.Println("BeforeCreate......")

    return nil

}


//插入数据

func Insert() {


    //实例化

    p := Product{

        Code:  "C001",

        Prize: 1000,

    }

    

    //插入

    res := db.Create(&p)

    if res.Error != nil {

        panic(res.Error) //错误

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    fmt.Printf("插入数据ID: %v\n", p.ID)

    

    //实例化

    p2 := Product{

        Code:  "C001",

        Prize: 1000,

    }

    

    //选择插入

    res2 := db.Select("Code").Create(&p2)

    if res2.Error != nil {

        panic(res2.Error) //错误

    }

    fmt.Printf("影响行数: %v\n", res2.RowsAffected)

    fmt.Printf("插入数据ID: %v\n", p2.ID)

    

    //批量插入

    products := []Product{{Code: "C1000"}, {Prize: 1000}, {Code: "C001000"}}

    res3 := db.Create(products)

    if res3.Error != nil {

        panic(res3.Error) //错误

    }

    fmt.Printf("影响行数: %v\n", res3.RowsAffected)

    

    //批量插入

    products4 := []Product{{Code: "C2000"}, {Prize: 2000}, {Code: "C002000"}, {Code: "C002000", Prize: 20000}}

    res4 := db.CreateInBatches(products4, 100)

    if res4.Error != nil {

        panic(res4.Error) //错误

    }

    fmt.Printf("影响行数: %v\n", res4.RowsAffected)

    

    //map插入:不会执行BeforeCreate

    res5 := db.Model(&Product{}).Create([]map[string]interface{}{

        {"Code": "map_01", "Prize": 1},

        {"Code": "map_02", "Prize": 2},

    })

    fmt.Printf("影响行数: %v\n", res5.RowsAffected)

}


//查询

func Find() {


    //实例化

    var p Product

    

    //查询

    db.First(&p, 2)

    res := db.First(&p, "code = ?", "C010")

    fmt.Printf("行数: %v\n", res.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //查询

    res1 := db.First(&p) //获取第一条

    res1 := db.Last(&p) //获取最后一条

    res1 := db.Take(&p) //获取一条数据

    fmt.Printf("res1: %v\n", res1.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //切片查询

    var p1 []Product

    ids := []int{1, 4, 3}

    res2 := db.Find(&p1, ids)

    fmt.Printf("行数: %v\n", res2.RowsAffected)

    for _, v := range p1 {

        fmt.Printf("v: %v\n", v.ID)

    }

    

    //查询全部

    var p3 []Product

    res3 := db.Where("id = ?", 71).Find(&p3)

    res3 := db.Where(&Product{Code: "map_01"}).Find(&p3) //结构体查询

    res3 := db.Where(map[string]interface{}{"Code": "map_02"}).Find(&p3) //map查询

    res3 := db.Where([]int{1, 2, 3, 4}).Find(&p3) //主键查询

    res3 := db.Find(&p3, []int{1, 2, 3, 4}) //where条件可以放在Find里面

    fmt.Printf("行数: %v\n", res3.RowsAffected)

    for _, v := range p3 {

        fmt.Printf("v: %v\n", v.ID)

    }

    

    db.Not() //取非

    db.Or() //或者

    db.Select() //字段

    db.Omit() //忽略

    db.Distinct() //唯一

    db.Order() //排序

    db.Limit() //限制条数

    db.Offset() //偏移量

    db.Group() //分组

    db.Having() //分组后筛选

    db.Joins() //关联

    db.Scan() //扫描到结构体

}


//更新前置钩子

func (p *Product) BeforeUpdate(tx *gorm.DB) (err error) {

    fmt.Println("BeforeUpdate......")

    return nil

}


//更新

func Update() {


    //实例化

    var p Product

    

    //查询

    db.First(&p, 71)

    

    //更新

    res := db.Model(&p).Update("Prize", 100)

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //批量更新

    res := db.Model(&p).Updates(Product{Code: "C100", Prize: 500}) //结构体更新

    res := db.Model(&p).Updates(map[string]interface{}{"Code": "C100", "Prize": 200}) //map更新

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //save更新

    p.Code = "666"

    p.Prize = 888

    res := db.Save(&p)

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //不查询数据更新

    res := db.Model(&Product{}).Where("id = ?", 71).Update("code", "not111")

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    fmt.Printf("ID: %v\n", p.ID)

    

    //选择某些字段更新

    db.Model(&p).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

    

    //忽略字段

    db.Model(&p).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

    

    //原生sql执行

    db.Exec("UPDATE users SET name = ?", "jinzhu")

    

    //sql表达式

    db.Model(&p).Update("price", gorm.Expr("price * ? + ?", 2, 100))

    

    //子查询更新

    db.Model(&p).Update("company_name", db.Model(&Product{}).Select("name").Where("companies.id = users.company_id"))

    

    //不执行Hook或者不自动更新时间:UpdateColumnUpdateColumns

    db.Model(&p).UpdateColumn("name", "hello")

    db.Model(&p).UpdateColumns(Product{Code: "hello", Prize: 18})

    

    //返回结果集到切片

    var users []Product

    db.Model(&users).Clauses(clause.Returning{}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))

}


//删除前置钩子

func (p *Product) BeforeDelete(tx *gorm.DB) (err error) {

    fmt.Println("BeforeDelete......")

    return nil

}


//删除

func Delete() {

    //实例化

    var p Product

    

    //主键删除

    res := db.Delete(&p, 3)

    res := db.Delete(&Product{}, 4)

    res := db.Delete(&Product{}, []int{5, 6, 7})

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    

    //批量删除

    res := db.Where("id IN ? ", []int{8, 9, 10}).Delete(&Product{})

    res := db.Delete(&Product{}, "id IN ? ", []int{11, 12})

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    

    //原始sql删除

    db.Exec("DELETE FROM products")

    

    //查询数据(包含软删除

    var p1 []Product

    res := db.Unscoped().Find(&p1)

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

    

    //真实删除

    res := db.Unscoped().Where("id=9").Delete(&Product{})

    if res.Error != nil {

         panic(res.Error)

    }

    fmt.Printf("影响行数: %v\n", res.RowsAffected)

}


2)、原生Sql、Sql构建器

//原生Sql

func Raw() {

    //结果结构体

    type Result struct {

        ID    int

        Code  string

        Prize uint

    }

    

    //原生查询

    var result Result

    db.Raw("select id,code,prize from products where id > ? order by id desc", 1).Scan(&result)

    fmt.Printf("result: %v\n", result)

    

    //原生统计

    var prize1 uint

    db.Raw("select sum(prize) from products where id > ? ", 1).Scan(&prize1)

    fmt.Printf("prize: %v\n", prize1)

    

    //更新返回切片

    var result1 []Result

    db.Raw("UPDATE products SET code = ? WHERE id >  ?", "DBRAW", 10).Scan(&result1)

    fmt.Printf("result1: %v\n", result1)

    

    //执行sql语句

    // db.Exec(sql)

    

    //命名参数

    var p []Product

    res := db.Where("code = @code", sql.Named("code", "DBRAW")).Find(&p)

    if res.Error != nil {

        panic(res.Error)

    }

    fmt.Printf("res: %v\n", res.RowsAffected)

    

    //转成sql语句不执行

    // stmt := db.Session(&gorm.Session{DryRun: true}).First(&p, 1).Statement

    // fmt.Printf("stmt.SQL.String(): %v\n", stmt.SQL.String())

    // fmt.Printf("stmt.Vars: %v\n", stmt.Vars)

    

    //ToSql

    sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {

        return tx.Model(&Product{}).Where("id = ?", 1).Limit(10).Find(&[]Product{})

    })

    fmt.Printf("sql: %v\n", sql)

    

    //Row、Rows

    var id int

    var code string

    var prize uint

    // row := db.Raw("select id,code,prize from products where id > ?", 1).Row()

    row := db.Table("products").Where("id > ?", 1).Select("id", "code", "prize").Row()

    row.Scan(&id, &code, &prize)

    fmt.Printf("id: %v\n", id)

    fmt.Printf("code: %v\n", code)

    fmt.Printf("prize: %v\n", prize)

    

    //rows

    rows, _ := db.Model(&Product{}).Select("id", "code", "prize").Where("id > ?", 20).Rows()

    defer rows.Close()

    var result3 []Result

    for rows.Next() {

        rows.Scan(&id, &code, &prize)

        fmt.Printf("id: %v\n", id)

        fmt.Printf("code: %v\n", code)

        fmt.Printf("prize: %v\n", prize)

        

        //扫入切片

        db.ScanRows(rows, &result3)

    }

    fmt.Printf("result3: %v\n", result3)

}


3)、关联关系

3-1)、Belongs To:belongs to 会与另一个模型建立了一对一的连接。 这种模型的每一个实例都“属于”另一个模型的一个实例。

文档:https://gorm.io/zh_CN/docs/belongs_to.html


3-2)、Has Onehas one 与另一个模型建立一对一的关联,但它和一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含拥有另一个模型的一个实例。

文档:https://gorm.io/zh_CN/docs/has_one.html


3-3)、Has Manyhas many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。

文档:https://gorm.io/zh_CN/docs/has_many.html


3-4)、Many to ManyMany to Many 会在两个 model 中添加一张连接表。

文档:https://gorm.io/zh_CN/docs/many_to_many.html


3-5)、实体关联在创建、更新记录时,GORM 会通过 Upsert 自动保存关联及其引用记录。

文档:https://gorm.io/zh_CN/docs/associations.html


3-7)、预加载GORM 允许在 Preload 的其它 SQL 中直接加载关系。

文档:https://gorm.io/zh_CN/docs/preload.html


关联标签:

标签                         描述

foreignKey              指定当前模型的列作为连接表的外键

references               指定引用表的列名,其将被映射为连接表外键

polymorphic           指定多态类型,比如模型名

polymorphicValue  指定多态值、默认表名

many2many            指定连接表表名

joinForeignKey        指定连接表的外键列名,其将被映射到当前表

joinReferences         指定连接表的外键列名,其将被映射到引用表

constraint                关系约束,例如:OnUpdate、OnDelet


3-7)、session会话配置GORM 提供了 Session 方法,这是一个 New Session Method,它允许创建带配置的新建会话模式,一次性会话

文档:https://gorm.io/zh_CN/docs/session.html

        // 会话参数

        type Session struct {

              DryRun                   bool

              PrepareStmt              bool

              NewDB                    bool

              Initialized              bool

              SkipHooks                bool

              SkipDefaultTransaction   bool

              DisableNestedTransaction bool

              AllowGlobalUpdate        bool

              FullSaveAssociations     bool

              QueryFields              bool

              Context                  context.Context

              Logger                   logger.Interface

              NowFunc                  func() time.Time

              CreateBatchSize          int

        }

// 新建会话模式

stmt := db.Session(&gorm.Session{DryRun: true}).First(&user, 1).Statement

stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id`

stmt.Vars         //=> []interface{}{1}


// 全局 DryRun 模式

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{DryRun: true})


3-8)、事务处理为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。

文档:https://gorm.io/zh_CN/docs/transactions.html

        

//gorm事务处理

func Transaction() {

    //手动事务

    p := Product{

        Code:  "Tran001",

        Prize: 100,

    }

    

    p1 := Product{

        Code:  "Tran002",

        Prize: 200,

    }

    

    //开启事务

    tx := db.Begin()

    res1 := tx.Create(&p)

    res2 := tx.Create(&p1)

    if res1.Error == nil && res2.Error == nil {

        //事务提交

        tx.Commit()

    } else {

        //事务回滚

        tx.Rollback()

    }

    

    //自动事务

    db.Transaction(func(tx *gorm.DB) error {

    

        p3 := Product{

            Code:  "Tran003",

            Prize: 300,

        }

        

        p4 := Product{

            Code:  "Tran004",

            Prize: 400,

        }

        

        //判断错误

        if err := tx.Create(&p3).Error; err != nil {

            return err

        }

        if err := tx.Create(&p4).Error; err != nil {

            return err

        }

        

        //提交事务

        return nil

    })

}


十三、常用设计模式及应用场景:设计模式是面向对象软件的设计经验,是通常设计问题的解决方案。每一种设计模式系统的命名、解释和评价了面向对象中一个重要的和重复出现的设计。

教程:https://www.bilibili.com/video/av679696203

笔记:https://golang-tech-stack.com/tutorial/design-pattern

package designmode

import (

    "fmt"

    "sync"

)


//工厂模式

//接口

type Fruit interface {

    Grant()

    Pick()

}


//Apple结构体

type Apple struct {

    Name string

}


func (o *Apple) Grant() {

    fmt.Println("种植", o.Name)

}


func (o *Apple) Pick() {

    fmt.Println("采摘", o.Name)

}


//Orange结构体

type Orange struct {

    Name string

}


func (o *Orange) Grant() {

    fmt.Println("种植", o.Name)

}


func (o *Orange) Pick() {

    fmt.Println("采摘", o.Name)

}


//工厂全局方法

func NeweFruit(t int, name string) Fruit {

    switch t {

            case 1:

            return &Apple{Name: name}

            case 2:

            return &Orange{Name: name}

            default:

            return nil

    }

}

//end 工厂模式


//单例模式

type Singleton interface {

    dosomething()

}


type singleton struct {

}


func (s *singleton) dosomething() {

    fmt.Println("单例模式")

}


var (

    once     sync.Once

    instance *singleton

)


//单例实现方法

func GetInstance() Singleton {

    once.Do(func() {

        instance = &singleton{}

    })

    return instance

}

//end 单例模式


十四、context:上下文,多个Goroutine共享数据,以及多Goroutine管理机制。是专门用来简化对于处理单个请求的多个goroutine之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。

教程:https://www.bilibili.com/video/BV19K411T7NL

参考:https://juejin.cn/post/6844903555145400334

参考:https://juejin.cn/post/6844903447771234317

微信图片_20220611233050.png

微信图片_20220611233449.png

Deadline:方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。

Done:方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。之后,Err 方法会返回一个错误,告知为什么 Context 被取消。

Err:方法返回取消的错误原因,因为什么Context被取消。

Value:方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。


十五、Protocol Buffers:现在的网络应用都是前后端分离的,数据传输方式有:json和xml两种格式,其中json更多一些。现在又多了一种数据传输方式,就是google开发的Protocol Buffers。在分布式应用或者微服务架构中,各个服务之间通常使用json或者xml结构数据进行通信,通常情况下,是没什么问题的,但是在高性能和大数据通信的系统当中,如果有办法可以压缩数据量,提高传输效率,显然会给用户带来更快更流畅的体验。

教程:https://www.bilibili.com/video/BV1Y3411j7EM

笔记:https://golang-tech-stack.com/tutorial/pb

1、下载:https://github.com/protocolbuffers/protobuf/releases

2、安装Protocol Buffers:下载对应系统版本

微信图片_20220612132532.png

微信图片_20220612132746.png

3、vscode安装扩展:vscode-proto3

4、安装Go Protocol Buffers插件:go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

5、GoPath的bin添加到环境变量中:

微信图片_20220612141357.png

6、第一个程序:

syntax = "proto3";

option go_package = "./;hello";


package hello;


message Person {

        string name = 1; //1、2、3表示编号,1-15是一个字节编码,16-2047是两个字节编码

        int32 age = 2;

        string email = 3;

}


7、编译生成文件: protoc --go_out=./proto ./proto/*


微信图片_20220612141852.png


8、枚举类型:

//版本3

syntax = "proto3";


//选项

option go_package="./;enums";


//包名

package enums;


//设置消息

message Enums {

        reserved 10, 23; //保留关键值

        string query = 1; //编号

        int32 page_number = 2;

        int32 result_per_page = 3;

        enum City { //枚举

                option allow_alias = true; //允许重复的值设置多个名称

                XIAMEN = 0; //枚举值,常量

                XM = 0;

                FUZHOU = 1;

                QUANZHOU = 2;

        }

        City citys = 4;

}


9、Protobuf序列化、反序列化:

//protobuf的序列化与反序列化

ar := &article.Article{

    Author:  "飓风呀",

    Date:    "2022-06-12",

    Page:    100,

    Version: "1.0",

}

fmt.Println(ar)


//序列化:转为二进制

bytes, _ := proto.Marshal(ar)

fmt.Printf("bytes: %v\n", bytes)


//反序列化

ar2 := &article.Article{}

err := proto.Unmarshal(bytes, ar2)

if err != nil {

    panic(err)

}

fmt.Printf("ar2: %v\n", ar2)

fmt.Printf("article.GetAuthor()=%v\n", ar2.GetAuthor())

fmt.Printf("article.GetDate()=%v\n", ar2.GetDate())

fmt.Printf("article.GetPage()=%v\n", ar2.GetPage())

fmt.Printf("article.GetVersion()=%v\n", ar2.GetVersion())


10、Protobuf与json的直接转换:

安装protojson:go get google.golang.org/protobuf/encoding/protojson

ar := &article.Article{

    Author:  "飓风呀",

    Date:    "2022-06-12",

    Page:    100,

    Version: "1.0",

}


//protojson

ar_interface := ar.ProtoReflect().Interface() //reflect反射


//message to json

j := protojson.Format(ar_interface)

fmt.Printf("j: %v\n", j)


//json to message

protojson.Unmarshal([]byte(j), ar_interface)

fmt.Printf("ar_interface: %v\n", ar_interface)


11、Protobuf定义服务:

syntax = "proto3";


option go_package = "./;article";


package article;


message Article {

        string author = 1;

        string date = 2;

        int32 page = 3;

        string version = 4;

}


service ArticleService{

        rpc login (Article) returns (Article);

        rpc register (Article) returns (Article);

}


更多笔记:golang学习笔记 - 标准包、依赖(四)


参考资料&教程

1、go get 快速导入GitHub中的包:https://www.jianshu.com/p/16aceb6369b6

2、goland编写go语言导入自定义包出现: package xxx is not in GOROOT (/xxx/xxx) 的解决方案:https://blog.csdn.net/qq_27184497/article/details/122160400

3、Vscode中Golang引入自定义包报错 package xxx is not in GOROOT:https://blog.csdn.net/qq_40209780/article/details/123133467

4、GO111MODULE的设置(及GOPROXY):https://www.cnblogs.com/pu369/p/12068645.html

5、关于go反射中的NumMethod方法的一点发现:https://www.bilibili.com/read/cv8985976/

6、VS Code 安装go插件失败分析及解决方案:https://blog.csdn.net/qq_36564503/article/details/124509832

7、VSCode: Could not import Golang package:https://stackoverflow.com/questions/58518588/vscode-could-not-import-golang-package

8、vscode 远程开发 go 提示 You are outside of a module and outside of $GOPATH/src:https://www.jianshu.com/p/089efae0bdd3


基础教程:

视频1:https://www.bilibili.com/video/BV1gf4y1r79E

笔记1:https://www.yuque.com/aceld/mo95lb/zwukev


视频2:https://www.bilibili.com/video/BV1s341147US

笔记2:https://www.bilibili.com/read/readlist/rl496566


教程:https://www.bilibili.com/video/BV1hv411x7we


进阶教程:

视频:https://www.bilibili.com/video/BV1zR4y1t7Wj

笔记:https://golang-tech-stack.com/tutorial/golang

只袄早~~~
感谢你的支持,我会继续努力!
扫码打赏,感谢您的支持!
golang 笔记 go Google 

文明上网理性发言!