對於業務層面的開發,就離不開數據庫的訪問。
1.創建項目
俗話說賣錢不賣錢,攤攤兒要扯圓,甭管怎樣,我們先建立一個標準的golang
項目,來訪問數據庫。
1.1 go mod 管理依賴
go mod init gitee.com/RandyField/sqltest
1.2 安裝mysql驅動包
go get -u github.com/go-sql-driver/mysql
1.3 創建文件
cd sqltest
New-Item main.go
New-Item service.go #數據庫訪問方法
New-Item models.go #數據映射結構
2.連接數據庫
main.go
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
const (
user = "root"
password = "111111"
database = "push-center"
protocol = "tcp"
address = "127.0.0.1"
charset = "utf8mb4"
parseTime = "True"
)
var db *sql.DB
func main() {
/*
https://pkg.go.dev/github.com/go-sql-driver/mysql
https://github.com/denisenkom/go-mssqldb
gorm-sqlx
*/
// Connstr := fmt.Sprintf("%s:%s@%s(%s)/%s?charset=%s&parseTime=%s",
// user, password, protocol, address, database, charset, parseTime)
// fmt.Println(Connstr)
// Connstr := "root:111111@tcp(127.0.0.1)/push-center?charset=utf8mb4&parseTime=True"
//parseTime 是查詢結果是否自動解析爲時間
//loc 是MySQL的時區設置
Connstr := "root:111111@tcp(127.0.0.1)/push-center?charset=utf8mb4&parseTime=True&loc=Local"
fmt.Println("start...")
var err error
db, err = sql.Open("mysql", Connstr)
if err != nil {
log.Fatalln(err.Error())
}
ctx := context.Background()
//驗證連接是否有效
err = db.PingContext(ctx)
if err != nil {
log.Fatalf(err.Error())
}
fmt.Println("Connected")
}
import "database/sql"
Golang
的database/sql
包提供了保證SQL或類SQL數據庫的泛用接口。我們的數據庫操作(編碼)也只在database/sql
包上進行。
import _ "github.com/go-sql-driver/mysql"
- 連接數據庫,需要加載目標數據庫的驅動,
Golang
是沒有提供官方的數據庫驅動,所有的數據庫驅動都是第三方驅動,但是它們都遵循sql.driver
包裏面定義的接口 - 我們並直接使用這個驅動,所以使用
_
引入,只需要在引入驅動包時調用包內init
函數進行自動註冊。
- 連接數據庫,需要加載目標數據庫的驅動,
實際上,需要使用
sql
包的Register()
數據庫驅動名稱
並實現
driver.Driver()
接口的struct
註冊:
sql.Register("mysql",&drv{})
-
Connstr
:username/password@tcp(ipaddress)/database?parm1=&parm2=
- 連接字符串,尤其注意後面的參數,博主在這裏使用
.net
的EFCore
插入時間,值總是正確,而在使用golang
時卻總是有問題(晚8小時),無論在代碼層面做何種轉換。parseTime
是查詢結果是否自動解析爲時間loc
是MySQL的時區設置
- 連接字符串,尤其注意後面的參數,博主在這裏使用
-
sql.Open()
:僅僅是配置連接,但並不真正連接,需要兩個參數:- 數據庫驅動名稱
- 數據庫連接字符串
- 返回一個執行
sql.DB
這個struct的指針:*sql.DB
這個指針纔是我們操作數據庫的關鍵鑰匙,我們編碼通過這個指針發送
sql
命令,獲得結果。它抽象了底層數據庫連接池並對其維護,且併發安全,這便意味着我們可以在多個goroutine
中併發使用。針對*sql.DB
有兩種用法:
- 定義全局變量,然後到處使用
- 定義變量,將其作爲參數傳遞給函數或者方法
-
ctx := context.Background()
:Context
(上下文)類型可以攜帶截止時間、取消信號和其他請求範圍的值,並且可以橫跨API邊界和進程。但是這裏的context
包的Background()
返回的Context很特殊,非nil
的空Context
,不會被取消也沒有值,沒有截止時間。- 通常用在main函數、初始化或測試中,作爲傳入請求的頂級
Context
- 通常用在main函數、初始化或測試中,作爲傳入請求的頂級
-
db.PingContext(ctx)
:驗證與數據庫的連接是否仍然有效,如有必要則建立一個連接。
3.訪問數據庫
訪問之前我們需要能夠映射數據庫表的struct
,但是struct
非必需條件。
models.go
package main
//Notifypush 推送通知
type notifypush struct {
Id int `json:"Id"`
AppName string `json:"APP"`
Target int `json:"Platform"`
TargetValue *string
PushType int
DeviceType int `json:"Device"`
Title *string
Body *string `json:"Content"`
CreateTime string
}
如果數據庫字段有Null,可空類型, 結構體或者變量,都需要定義指針類型,否則會發生運行時錯誤。
3.1 查詢單條
service.go
package main
import (
"log"
"time"
)
// GetById 根據ID獲取單條數據
func GetById(id int) (notifypush, error) {
n := notifypush{}
err := db.QueryRow("select Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notifypush where Id=?",
id).Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
// err := db.QueryRow("select Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notitypush where Id=@Id",
// sql.Named("Id", id)).Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
return n, err
}
如果是
sqlserver
,參數是使用@+參數名來進行站位,並配合sql.Named()
函數使用。mysql
不能這樣,否則會報錯mysql: driver does not support the use of Named Parameters
3.2 查詢多條
// GetMultiRow 獲取多條數據
func GetMultiRow(id int) (ns []notifypush, err error) {
rows, err := db.Query("SELECT Id,AppName,Target,TargetValue,PushType,DeviceType,Title,Body,CreateTime From notifypush WHERE Id>?",
id)
// 非常重要:關閉rows釋放持有的數據庫鏈接
defer rows.Close()
for rows.Next() {
n := notifypush{}
err = rows.Scan(&n.Id, &n.AppName, &n.Target, &n.TargetValue, &n.PushType, &n.DeviceType, &n.Title, &n.Body, &n.CreateTime)
if err != nil {
log.Fatalln(err.Error())
}
ns = append(ns, n)
}
return
}
3.2 修改、插入、刪除
修改、插入、刪除都是cmd,在sql
包中只有一個方法:Exec
,這裏就省略delete
操作,實際業務上很少使用物理刪除。
Update
Update操作,需要定義方法(結構體爲接收者)
//Update 更新
func (push *notifypush) Update() error {
_, err := db.Exec("UPDATE notifypush SET AppName=?,Target=?,PushType=?,DeviceType=? WHERE Id=?",
push.AppName, push.Target, push.PushType, push.DeviceType, push.Id)
return err
}
Insert
//Insert 插入
func Insert() error {
// _, err := db.Exec("INSERT INTO notifypush(AppName,Target,PushType,DeviceType,CreateTime) VALUES(?,?,?,?,?)",
// "test-insert-app", 1, 2, 3, time.Now())
// 改用 prepare
sql := `INSERT INTO notifypush(AppName,Target,PushType,DeviceType,CreateTime) VALUES(?,?,?,?,?)`
stmt, err := db.Prepare(sql)
if err != nil {
log.Fatalln(err.Error())
}
defer stmt.Close()
_, err = stmt.Exec("insert-app", 1, 2, 3, time.Now())
return err
}
4.調用方法
4.1 編碼
main.go
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
const (
user = "root"
password = "111111"
database = "push-center"
protocol = "tcp"
address = "127.0.0.1"
charset = "utf8mb4"
parseTime = "True"
)
var db *sql.DB
func main() {
//ommit connect code
//查詢
notify, err := GetById(10)
if err != nil {
log.Fatalf(err.Error())
}
data, err := json.Marshal(notify)
fmt.Printf("推送消息爲:%s\n", data)
//更新
notify.AppName = "smart"
err = notify.Update()
if err != nil {
log.Fatalf(err.Error())
}
notify, err = GetById(10)
if err != nil {
log.Fatalf(err.Error())
}
data, err = json.Marshal(notify)
fmt.Printf("更新後:%s\n", data)
//插入
err = Insert()
if err != nil {
log.Fatalf(err.Error())
}
//查詢多條
ns, err := GetMultiRow(24)
datas, err := json.Marshal(ns)
fmt.Printf("推送消息爲:%s\n", datas)
}
4.2 運行
這裏我們的項目是一個具有多個文件.go
的module
,所以不能簡單使用go run main.go
。需要先編譯,才能運行
go build #編譯會生成sqltest.exe
.\sqltest.exe #運行
5.ORM
5.1 GORM
GORM是GoLang
中最出色的ORM
框架,支持MySQL
、PostgreSQL
、Sqlite
、SQL Server
,功能非常強大,也可以直接執行SQL並獲取結果集。還有數據庫遷移。博主把他看作Golang
版本的EntityFramework
。
5.2 Sqlx
Sqlx是對GoLang
標準database/sql
的擴展。其特點是:
- 把SQL執行的結果集轉化成數據結構(
Struct
、Maps
、Slices
)。 - 支持問號(?)或命名的
Prepared Statements
,避免SQL注入的安全問題
在博主看來,這個更像是一個golang
版本的dapper
。
參考連接
https://www.bilibili.com/video/BV1dZ4y1577v
https://qinzhiqiang.cn/2019/10/golang服務常用組件-gorm-sqlx-mysql-mongodb/
作者:Garfield
同步更新至個人博客:http://www.randyfield.cn/
本文版權歸作者和博客園共有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯繫[email protected].