生活札记
golang学习笔记 - 高阶(三)
一、常用命令
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())
}
十、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/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或者不自动更新时间:UpdateColumn、UpdateColumns
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 One:has one 与另一个模型建立一对一的关联,但它和一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例。
文档:https://gorm.io/zh_CN/docs/has_one.html
3-3)、Has Many:has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。
文档:https://gorm.io/zh_CN/docs/has_many.html
3-4)、Many to Many:Many 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
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:下载对应系统版本
3、vscode安装扩展:vscode-proto3
4、安装Go Protocol Buffers插件:go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
5、GoPath的bin添加到环境变量中:
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/*
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);
}
参考资料&教程
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
进阶教程:
文明上网理性发言!
已完结!