Go · 2 分钟阅读
Go 操作数据
目录
数据库
database/sql 是标准库的统一接口,配驱动用。MySQL 配 github.com/go-sql-driver/mysql:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// sql.Open 只验证参数格式,真正建连要 Ping 一下
if err := db.Ping(); err != nil {
log.Fatal(err)
}
rows, err := db.Query("SELECT id, name FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %s\n", id, name)
}
result, err := db.Exec("INSERT INTO users(name) VALUES(?)", "John Doe")
if err != nil {
log.Fatal(err)
}
id, err := result.LastInsertId()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Inserted ID: %d\n", id)
}
sql.Open 不会真的连数据库,只是检查 DSN 格式对不对,得 Ping() 一下才能确认通不通。新手经常踩这个坑,写了 Open 就以为连上了。
错误处理用 log.Fatal 比 panic 优雅一点,至少会打印错误信息再退出,调试方便。
文件读写
Go 1.16 起 ioutil 包废弃了,读写文件直接用 os 包:
package main
import (
"fmt"
"log"
"os"
)
func main() {
data := []byte("Hello, Go!")
if err := os.WriteFile("example.txt", data, 0644); err != nil {
log.Fatal(err)
}
content, err := os.ReadFile("example.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(content))
if err := os.Remove("example.txt"); err != nil {
log.Fatal(err)
}
}
os.WriteFile 和 os.ReadFile 就是原来 ioutil 那两个函数搬过来了,签名没变,换个包名的事。老项目里看到 ioutil 不用慌,能跑,只是新代码别再那么写。
并发
goroutine 加 WaitGroup,最朴素的并发模式:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
wg.Done() 用 defer 保证函数退出时一定执行,就算 panic 也能减一。两个坑新手最爱踩:忘了 Add(1),Wait() 提前返回;忘了 Done(),Wait() 死锁。