【go学习笔记五】标准库、数据库基本操作、gorm
golang标准库
bytes
提供了对字节切片进行读写操作的一系列函数。如比较、后缀检查、索引函数等。
类型强制转换
var i int = 100
var b byte = 10
b = byte(i)
fmt.Printf("string(b): %v\n", string(b)) // string(b): d ascii对照值:100
b2 := []byte{100, 101, 102}
var s string = string(b2)
fmt.Printf("s: %v\n", s) // s: def
b2 = []byte(s)
fmt.Printf("b2: %v\n", b2) // b2: [100 101 102]
基本函数
bytes.Contains
:
s := "orzlinux.cn"
b := []byte(s)
// b1 := []byte("hqinglau")
// b2 := []byte("HQINGLAU")
// strings也有这个方法
fmt.Printf("strings.Contains(\"Hello\", \"lo\"): %v\n", strings.Contains("Hello", "lo"))
// strings.Contains("Hello", "lo"): true
b3 := bytes.Contains(b, []byte("linux")) // b3: true
fmt.Printf("b3: %v\n", b3)
bytes.Count
:
s := "orzlinux.cn-hqinglau-li"
b := []byte(s)
i := bytes.Count(b, []byte("li"))
fmt.Printf("i: %v\n", i) // i: 2
bytes.Repeat
:
s := "orzlinux.cn-"
b := []byte(s)
b2 := bytes.Repeat(b, 3)
fmt.Printf("b2: %v\n", string(b2))
// b2: orzlinux.cn-orzlinux.cn-orzlinux.cn-
bytes.Replace
:
替换。
bytes.Runes(s)
:
考虑非英文字符:
func main() {
s := "雷雅迪"
b := []byte(s)
r := bytes.Runes(b) // []rune类型
fmt.Printf("len(b): %v\n", len(b))
fmt.Printf("b[0]: %v\n", b[0])
fmt.Printf("len(r): %v\n", len(r))
fmt.Printf("r[0]: %v\n", r[0])
}
// len(b): 9
// b[0]: 233
// len(r): 3
// r[0]: 38647
bytes.Join
:
b := [][]byte{[]byte("你好"), []byte("世界")}
b2 := []byte(",")
fmt.Println(string(bytes.Join(b, b2))) // 你好,世界
Reader类型
Reader实现了io.Reader
, io.ReaderAt
, io.WriterTo
, io.Seeker
, io.ByteScanner
, io.RuneScanner
接口,只读,可以seek。
func main() {
data := "123456789"
// 通过[]byte创建Reader
re := bytes.NewReader([]byte(data))
// 返回魏都区部分的长度
fmt.Println("re len: ", re.Len())
// 返回底层数据总长度
fmt.Println("re size: ", re.Size())
fmt.Println("---------------")
buf := make([]byte, 2)
for {
n, err := re.Read(buf)
if err != nil {
fmt.Printf("err: %v\n", err)
break
}
fmt.Println(string(buf[:n]))
}
fmt.Println("-----------------")
// 设置偏移量,因为上面的操作已经修改了读取位置信息
re.Seek(0, 0)
for {
b, err := re.ReadByte()
if err != nil {
fmt.Printf("err: %v\n", err)
break
}
fmt.Println(string(b))
}
fmt.Println("-----------------")
re.Seek(0, 0)
off := int64(0)
for {
n, err := re.ReadAt(buf, off)
if n == 0 { // 只判断err的话,最后一茬会漏掉
fmt.Printf("err: %v\n", err)
break
}
off += int64(n)
fmt.Println(off, string(buf[:n]))
}
}
// re len: 9
// re size: 9
// ---------------
// 12
// 34
// 56
// 78
// 9
// err: EOF
// -----------------
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// err: EOF
// -----------------
// 2 12
// 4 34
// 6 56
// 8 78
// 9 9
// err: EOF
Buffer类型
var b bytes.Buffer
// type Buffer struct {
// buf []byte // contents are the bytes buf[off : len(buf)]
// off int // read at &buf[off], write at &buf[len(buf)]
// lastRead readOp // last read operation, so that Unread* can work correctly.
// }
fmt.Printf("b: %v\n", b) // b: {[] 0 0}
b2 := bytes.NewBufferString("hiyou")
fmt.Printf("b2: %v\n", b2) // b2: hiyou
n, _ := b.WriteString("hello")
fmt.Printf("string(b.Bytes()): %v\n", string(b.Bytes()[:n]))
errors
type error interface {
Error() string
}
不一定是一个错误,可以是任何信息,例如io.EOF表示数据读取结束,而不是遇到了什么错误。
func check(v int) (int, error) {
if v <= 0 {
return 0, errors.New("输入错误:应大于0")
} else {
return v, nil
}
}
func main() {
fmt.Println("1")
_, err := check(-1)
if err != nil {
fmt.Printf("err: %v\n", err)
}
fmt.Println("2")
}
// 1
// err: 输入错误:应大于0
// 2
sort
提供了排序切片和自定义数据集以及相关功能的函数
简单int切片排序:
s := []int{2, 4, 1, 3}
fmt.Printf("sort.IntsAreSorted(s): %v\n", sort.IntsAreSorted(s))
sort.Ints(s)
fmt.Printf("s: %v\n", s) // s: [1 2 3 4]
自定义类型
应实现Len()
, Less()
, Swap()
方法
type Interface interface {
// Len is the number of elements in the collection.
Len() int
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
接收类型不能为切片:
但是type一下就可以了:
自定义结构体排序例子:
type Person struct {
name string
age int
}
type PersonSlice []Person
func (p PersonSlice) Len() int {
return len(p)
}
func (p PersonSlice) Less(i, j int) bool {
if p[i].name == p[j].name {
return p[i].age < p[j].age
} else {
return p[i].name < p[j].name
}
}
func (p PersonSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func main() {
s := PersonSlice{
Person{
name: "yadi",
age: 23,
},
Person{
name: "hqing",
age: 20,
},
Person{
name: "yadi",
age: 21,
},
}
sort.Sort(s)
fmt.Printf("s: %v\n", s)
// s: [{hqing 20} {yadi 21} {yadi 23}]
}
time
基本使用
func main() {
t := time.Now()
fmt.Printf("t: %v\n", t)
// t: 2022-04-19 13:17:14.2850915 +0800 CST m=+0.003264801
fmt.Printf("t.Year(): %v\n", t.Year()) // t.Year(): 2022
fmt.Printf("t.Month(): %s\n", t.Month()) // t.Month(): April
fmt.Printf("t.Month(): %d\n", t.Month()) // t.Month(): 4
fmt.Printf("t.Day(): %v\n", t.Day()) // t.Day(): 19
}
时间戳
1970年1月1日至当前时间的总毫秒数。
t := time.Now()
fmt.Printf("t.Unix(): %v\n", t.Unix()) // t.Unix(): 1650345665
其他
t := time.Now()
t1 := t.Add(time.Hour * 24)
fmt.Printf("t1: %v\n", t1)
// t1: 2022-04-20 13:28:22.1204393 +0800 CST m=+86400.003243801
t2 := t1.Sub(t)
fmt.Printf("t2: %v\n", t2) // t2: 24h0m0s
b := t.Equal(t1)
fmt.Printf("b: %v\n", b) // b: false
// Before
// After
// Ticker
// Format 必须用这个时间格式化(go的诞生时间),不然不对
fmt.Println(t.Format("2006-01-02 03:04"))
// 2022-04-19 01:32
fmt.Println(t.Format("2006-01-02 03:04:05.000 PM Mon Jan"))
// 2022-04-19 01:32:11.888 PM Tue Apr
// ParseInLocation解析时间
encoding
json
json的编解码,将json字符串转换为struct,或者将struct转换为json。
type Person struct {
Name string
Age int
Email string
}
func main() {
p := Person{
Name: "tom",
Age: 20,
Email: "tom@gmailcom",
}
b, _ := json.Marshal(p)
fmt.Printf("string(b): %v\n", string(b))
// string(b): {"Name":"tom","Age":20,"Email":"tom@gmailcom"}
s := `{"Name":"tom","Age":20,"Email":"tom@gmailcom"}`
var m Person
json.Unmarshal([]byte(s), &m)
fmt.Printf("m: %#v\n", m)
// m: main.Person{Name:"tom", Age:20, Email:"tom@gmailcom"}
}
xml
类似json
math
func main() {
fmt.Printf("math.MaxFloat64: %v\n", math.MaxFloat64)
fmt.Printf("math.Pi: %v\n", math.Pi)
fmt.Printf("math.MaxInt32: %v\n", math.MaxInt32)
}
// math.MaxFloat64: 1.7976931348623157e+308
// math.Pi: 3.141592653589793
// math.MaxInt32: 2147483647
随机数
import (
"fmt"
"math"
"math/rand"
)
func main() {
fmt.Printf("math.Abs(-3.14): %v\n", math.Abs(-3.14))
fmt.Printf("math.Pow(2, 10): %v\n", math.Pow(2, 10))
fmt.Printf("math.Sqrt(64): %v\n", math.Sqrt(64))
fmt.Printf("math.Cbrt(27): %v\n", math.Cbrt(27)) // 开立方
fmt.Printf("math.Ceil(3.14): %v\n", math.Ceil(3.14)) // 向上取整
fmt.Printf("math.Floor(8.75): %v\n", math.Floor(8.75)) // 向下取整
fmt.Printf("math.Mod(10, 3): %v\n", math.Mod(10, 3)) // 取余
// 取整数和小数部分
int2, frac := math.Modf(3.1415)
fmt.Printf("%f, %f\n", int2, frac) // 3.000000, 0.141500
// 随机数
rand.Seed(1)
fmt.Printf("rand.Int: %d\n", rand.Int())
fmt.Printf("rand.Int: %d\n", rand.Int())
// rand.Int: 5577006791947779410
// rand.Int: 8674665223082153551
fmt.Printf("rand.Int: %d\n", rand.Intn(100)) // rand.Int: 47
}
操作数据库
创建数据
mysql> select database();
+------------+
| database() |
+------------+
| go_db |
+------------+
1 row in set (0.00 sec)
表信息:
安装包:
go.dev网站下的package:
go get -u github.com/go-sql-driver/mysql
连接数据库
当导入一个包时,包下文件里所有的init函数都会执行,但是有时候不需要将整个包都导入进去,仅仅是希望它执行init函数,这个时候就可以使用
import _ 包路径
引用这个包,无法通过包名调用包的其它函数。
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
// 定义一个全局对象db
var db *sql.DB
func initDB() (err error) {
dsn := "root:xxx@tcp(42.193.121.128:3306)/go_db?charset=utf8mb4&parseTime=True"
// open不会校验账户密码是否正确
db, err = sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
print(db)
// 尝试与数据库简历连接(检查dsn是否正确)
// 为了让连接错误在初始化阶段就暴露出来
err = db.Ping()
if err != nil {
return err
}
// 最大连接时长
db.SetConnMaxLifetime(time.Minute * 3)
// 最大连接数
db.SetMaxOpenConns(10)
// 空闲连接数
db.SetMaxIdleConns(10)
return nil
}
func main() {
err := initDB()
if err != nil {
fmt.Printf("err: %v\n", err)
return
} else {
fmt.Println("初始化成功")
// 0xc0000a6c30初始化成功
}
}
插入数据
func main() {
err := initDB()
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Println("数据库初始化成功!")
sqlStr := "insert into user_tbl(username,password) values(?,?)"
r, err := db.Exec(sqlStr, "hqinglau", "nopwd")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
i, _ := r.LastInsertId()
fmt.Printf("i: %v\n", i) // i: 3
}
}
查询单行
sqlStr := "select * from user_tbl where id=?"
var u User
err2 := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.username, &u.password)
if err2 != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("u: %v\n", u) // u: {1 kite 456}
}
查询多行
sqlStr := "select * from user_tbl where id>?"
var u User
r, err2 := db.Query(sqlStr, 1)
if err2 != nil {
fmt.Printf("err: %v\n", err)
} else {
for r.Next() {
r.Scan(&u.id, &u.username, &u.password)
fmt.Printf("u: %v\n", u)
}
}
更新数据
sqlStr:="update user_tbl set username=?,password=? where id=?"
r, err := db.Exec(sqlStr, "big tom", "456", 2)
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
i, _ := r.RowsAffected()
fmt.Printf("受影响的行: %v\n", i) // 受影响的行: 1
}
再进行查询:
删除数据
仍然是db.Exec()
sqlStr:="delete from user_tbl where id=?"
r, err := db.Exec(sqlStr, 3)
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
i, _ := r.RowsAffected()
fmt.Printf("受影响的行: %v\n", i)
}
gorm
对象关系映射(Object Relational Mapping),ORM,解决面向对象和关系数据库存在的互不匹配的现象。
$ go get -u gorm.io/gorm
$ go get -u gorm.io/driver/mysql
模型
例子:
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Product struct {
gorm.Model // 继承,包括id, 创建,更新,删除时间
Code string
Price uint
}
// type Model struct {
// ID uint `gorm:"primarykey"`
// CreatedAt time.Time
// UpdatedAt time.Time
// DeletedAt DeletedAt `gorm:"index"`
// }
标签:
type Model struct {
ID uint `gorm:"primaryKey;column:id"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
连接到数据库
dsn := "root:***@tcp(42.193.121.128:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
官方支持的数据库类型有:MySQL,PostgreSQL,SQLite, SQL Server
根据结构体创建表
func main() {
dsn := "root:***@tcp(42.193.121.128:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
// 创建表
db.AutoMigrate(&Product{})
}
插入数据
// 插入数据
p := Product{
Code: "1001",
Price: 100,
}
db.Create(&p)
查询
var p Product
db.First(&p, 1) // 根据主键查询
fmt.Printf("p: %v\n", p)
// p: {{1 2022-04-20 13:18:48.959 +0800 CST 2022-04-20 13:18:48.959
// +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} 1001 100}
db.First(&p, "code=?", "1001") // 根据列查询
fmt.Printf("p: %v\n", p) // 同上
更新
var p Product
db.First(&p, 1)
db.Model(&p).Update("Price", 1000)
更新多个字段:
db.Model(&p).Updates(Product{Price: 200, Code: "AC101"})
db.Model(&p).Updates(map[string]interface{}{"Price": 200, "Code": "ada"})
删除
var p Product
db.First(&p, 1)
db.Delete(&p)
// [221.482ms] [rows:1] UPDATE `products` SET `deleted_at`='2022-04-20 13:33:24.912'
// WHERE `products`.`id` = 1 AND `products`.`deleted_at` IS NULL
软删除,只添加了删除标记。