Go 連接 MySQL實作增刪改查
一、初始化連接
創建專案

配置 Environment
https://goproxy.cn,direct
MySQL 資料庫驅動
MySQL驅動https://github.com/go-sql-driver/mysql
go get -u github.com/go-sql-driver/mysql
main.go 檔案 初始化連接
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
func main() {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
db, err := sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
panic(err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
defer db.Close()
fmt.Println("connect to database") // 列印這句話并不能表示資料庫已經連上了
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go mod tidy
go: finding module for package github.com/go-sql-driver/mysql
go: found github.com/go-sql-driver/mysql in github.com/go-sql-driver/mysql v1.7.1
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database
Code/go/mysql_demo via ?? v1.20.3 via ?? base
?
sql.Open 原始碼
// Open may just validate its arguments without creating a connection
// to the database. To verify that the data source name is valid, call
// Ping.
// Open 可能只驗證其引數,而不創建與資料庫的連接,若要驗證資料源名稱是否有效,請呼叫 Ping,
//
// The returned DB is safe for concurrent use by multiple goroutines
// and maintains its own pool of idle connections. Thus, the Open
// function should be called just once. It is rarely necessary to
// close a DB.
// 回傳的資料庫可以安全地由多個 goroutines 并發使用,并維護自己的空閑連接池,因此,Open 函式應該只呼叫一次,很少需要關閉資料庫,
func Open(driverName, dataSourceName string) (*DB, error) {
driversMu.RLock()
driveri, ok := drivers[driverName]
driversMu.RUnlock()
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
if driverCtx, ok := driveri.(driver.DriverContext); ok {
connector, err := driverCtx.OpenConnector(dataSourceName)
if err != nil {
return nil, err
}
return OpenDB(connector), nil
}
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}
初始化連接 驗證資料源名稱是否有效,呼叫 Ping
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
func main() {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
db, err := sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
panic(err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
defer db.Close()
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return
}
fmt.Println("connect to database success") //
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
Code/go/mysql_demo via ?? v1.20.3 via ?? base took 2.5s
?
db.Ping() 原始碼
// PingContext verifies a connection to the database is still alive,
// establishing a connection if necessary.
// PingContext 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
func (db *DB) PingContext(ctx context.Context) error {
var dc *driverConn
var err error
err = db.retry(func(strategy connReuseStrategy) error {
dc, err = db.conn(ctx, strategy)
return err
})
if err != nil {
return err
}
return db.pingDC(ctx, dc, dc.releaseConn)
}
// Ping verifies a connection to the database is still alive,
// establishing a connection if necessary.
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
//
// Ping uses context.Background internally; to specify the context, use
// PingContext.
func (db *DB) Ping() error {
return db.PingContext(context.Background())
}
優化之后初始化連接完整代碼:
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
}
SetMaxIdleConns 和 SetMaxOpenConns 原始碼
// SetMaxIdleConns sets the maximum number of connections in the idle
// connection pool.
//
// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns,
// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit.
//
// If n <= 0, no idle connections are retained.
//
// The default max idle connections is currently 2. This may change in
// a future release.
func (db *DB) SetMaxIdleConns(n int) {
db.mu.Lock()
if n > 0 {
db.maxIdleCount = n
} else {
// No idle connections.
db.maxIdleCount = -1
}
// Make sure maxIdle doesn't exceed maxOpen
if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen {
db.maxIdleCount = db.maxOpen
}
var closing []*driverConn
idleCount := len(db.freeConn)
maxIdle := db.maxIdleConnsLocked()
if idleCount > maxIdle {
closing = db.freeConn[maxIdle:]
db.freeConn = db.freeConn[:maxIdle]
}
db.maxIdleClosed += int64(len(closing))
db.mu.Unlock()
for _, c := range closing {
c.Close()
}
}
// SetMaxOpenConns sets the maximum number of open connections to the database.
//
// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than
// MaxIdleConns, then MaxIdleConns will be reduced to match the new
// MaxOpenConns limit.
//
// If n <= 0, then there is no limit on the number of open connections.
// The default is 0 (unlimited).
func (db *DB) SetMaxOpenConns(n int) {
db.mu.Lock()
db.maxOpen = n
if n < 0 {
db.maxOpen = 0
}
syncMaxIdle := db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen
db.mu.Unlock()
if syncMaxIdle {
db.SetMaxIdleConns(n)
}
}
Database 與 MySQL 注冊驅動

二、Database SQL CRUD 增刪改查
創建資料庫后建表并插入資料
~ via ?? base
? mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 451
Server version: 8.0.32 Homebrew
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| mysql_test |
| performance_schema |
| sql_test |
| sys |
+--------------------+
11 rows in set (0.02 sec)
mysql> use sql_test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+--------------------+
| Tables_in_sql_test |
+--------------------+
| user |
+--------------------+
1 row in set (0.00 sec)
mysql> desc user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | | |
| age | int | YES | | 0 | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> show create table user \G;
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT '',
`age` int DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
ERROR:
No query specified
mysql> select * from user;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 小喬 | 16 |
| 2 | 小喬 | 12 |
+----+--------+------+
2 rows in set (0.00 sec)
mysql>
單行查詢
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
type user struct {
id int
age int
name string
}
// 查詢單條資料
func queryRowDemo(id int) (user, error) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// QueryRow 執行預期最多回傳一行的查詢,
// QueryRow 始侄訓傳一個非 nil 值,錯誤將延遲到呼叫 row 的 Scan 方法,
// Scan 將匹配行中的列復制到 dest 指向的值中
// 如果多行與查詢匹配,Scan 將使用第一行并丟棄其余行,
// 如果沒有與查詢匹配的行,Scan 將回傳 ErrNoRows,
// QueryRow 之后要呼叫 Scan 方法,否則資料庫連接不會被釋放
// Scan 原始碼:defer r.rows.Close()
err := db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err: %v\n", err)
return u, err
}
return u, nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
u, err := queryRowDemo(1)
if err != nil {
fmt.Printf("query row failed, err: %v", err)
}
fmt.Printf("id: %d name: %s age: %d\n", u.id, u.name, u.age)
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
id: 1 name: 小喬 age: 16
多行查詢
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
type user struct {
id int
age int
name string
}
// 查詢單條資料
func queryRowDemo(id int) (user, error) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// QueryRow 執行預期最多回傳一行的查詢,
// QueryRow 始侄訓傳一個非 nil 值,錯誤將延遲到呼叫 row 的 Scan 方法,
// Scan 將匹配行中的列復制到 dest 指向的值中
// 如果多行與查詢匹配,Scan 將使用第一行并丟棄其余行,
// 如果沒有與查詢匹配的行,Scan 將回傳 ErrNoRows,
// QueryRow 之后要呼叫 Scan 方法,否則資料庫連接不會被釋放
// Scan 原始碼:defer r.rows.Close()
err := db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err: %v\n", err)
return u, err
}
return u, nil
}
// 查詢多條資料
func queryMultiRowDemo(id int) {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, id)
if err != nil {
fmt.Printf("query failed, err: %v\n", err)
return
}
// 關閉 rows 釋放持有的資料庫鏈接
// Close 關閉行,防止進一步列舉,
// 如果呼叫 Next 并回傳 false 并且沒有其他結果集,
// 則行將自動關閉,檢查 Err. 的結果就足夠了, 關閉是冪等的,不會影響 Err 的結果,
// 因為不能保證 for rows.Next() 一定可以執行完,有可能會 panic 或其他情況,
// 故需要在此 defer 關閉連接
defer rows.Close()
// 回圈讀取結果集中的資料
// Next 準備下一個結果行,以便使用 Scan 方法讀取,
// 成功時回傳 true,如果沒有下一個結果行或在準備時發生錯誤,則回傳 false,
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
// Close 將關閉資料庫并阻止啟動新查詢,關閉,然后等待服務器上已開始處理的所有查詢完成,
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
// 查詢單行資料
//u, err := queryRowDemo(1)
//if err != nil {
// fmt.Printf("query row failed, err: %v", err)
//}
//fmt.Printf("id: %d name: %s age: %d\n", u.id, u.name, u.age)
// 查詢多行資料
queryMultiRowDemo(0)
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
id:1 name:小喬 age:16
id:2 name:小喬 age:12
Code/go/mysql_demo via ?? v1.20.3 via ?? base
?
插入資料
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
type user struct {
id int
age int
name string
}
// 查詢單條資料
func queryRowDemo(id int) (user, error) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// QueryRow 執行預期最多回傳一行的查詢,
// QueryRow 始侄訓傳一個非 nil 值,錯誤將延遲到呼叫 row 的 Scan 方法,
// Scan 將匹配行中的列復制到 dest 指向的值中
// 如果多行與查詢匹配,Scan 將使用第一行并丟棄其余行,
// 如果沒有與查詢匹配的行,Scan 將回傳 ErrNoRows,
// QueryRow 之后要呼叫 Scan 方法,否則資料庫連接不會被釋放
// Scan 原始碼:defer r.rows.Close()
err := db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err: %v\n", err)
return u, err
}
return u, nil
}
// 查詢多條資料
func queryMultiRowDemo(id int) {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, id)
if err != nil {
fmt.Printf("query failed, err: %v\n", err)
return
}
// 關閉 rows 釋放持有的資料庫鏈接
// Close 關閉行,防止進一步列舉,
// 如果呼叫 Next 并回傳 false 并且沒有其他結果集,
// 則行將自動關閉,檢查 Err. 的結果就足夠了, 關閉是冪等的,不會影響 Err 的結果,
// 因為不能保證 for rows.Next() 一定可以執行完,有可能會 panic 或其他情況,
// 故需要在此 defer 關閉連接
defer rows.Close()
// 回圈讀取結果集中的資料
// Next 準備下一個結果行,以便使用 Scan 方法讀取,
// 成功時回傳 true,如果沒有下一個結果行或在準備時發生錯誤,則回傳 false,
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
}
// 插入資料
func insertRowDemo(name string, age int) (int64, error) {
sqlStr := "INSERT INTO user (name, age) VALUES (?,?)"
result, err := db.Exec(sqlStr, name, age)
if err != nil {
fmt.Printf("insert failed, err: %v\n", err)
return 0, err
}
// LastInsertId 回傳資料庫為回應命令而生成的整數,
// 通常,這將來自插入新行時的“自動增量”列,
// 并非所有資料庫都支持此功能,并且此類陳述句的語法各不相同,
var newID int64
newID, err = result.LastInsertId() // 新插入資料的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err: %v\n", err)
return 0, err
}
return newID, nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
// Close 將關閉資料庫并阻止啟動新查詢,關閉,然后等待服務器上已開始處理的所有查詢完成,
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
// 查詢單行資料
//u, err := queryRowDemo(1)
//if err != nil {
// fmt.Printf("query row failed, err: %v", err)
//}
//fmt.Printf("id: %d name: %s age: %d\n", u.id, u.name, u.age)
// 查詢多行資料
//queryMultiRowDemo(0)
// 插入資料
newID, err := insertRowDemo("小跟班", 12)
if err != nil {
fmt.Printf("insert row failed, err: %v", err)
}
fmt.Printf("insert success, the id is %d.\n", newID)
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
insert success, the id is 4.
Code/go/mysql_demo via ?? v1.20.3 via ?? base
?
SQL 查詢 插入結果
mysql> select * from user;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | 小喬 | 16 |
| 2 | 小喬 | 12 |
| 4 | 小跟班 | 12 |
+----+-----------+------+
3 rows in set (0.00 sec)
mysql>
更新資料
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
type user struct {
id int
age int
name string
}
// 查詢單條資料
func queryRowDemo(id int) (user, error) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// QueryRow 執行預期最多回傳一行的查詢,
// QueryRow 始侄訓傳一個非 nil 值,錯誤將延遲到呼叫 row 的 Scan 方法,
// Scan 將匹配行中的列復制到 dest 指向的值中
// 如果多行與查詢匹配,Scan 將使用第一行并丟棄其余行,
// 如果沒有與查詢匹配的行,Scan 將回傳 ErrNoRows,
// QueryRow 之后要呼叫 Scan 方法,否則資料庫連接不會被釋放
// Scan 原始碼:defer r.rows.Close()
err := db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err: %v\n", err)
return u, err
}
return u, nil
}
// 查詢多條資料
func queryMultiRowDemo(id int) {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, id)
if err != nil {
fmt.Printf("query failed, err: %v\n", err)
return
}
// 關閉 rows 釋放持有的資料庫鏈接
// Close 關閉行,防止進一步列舉,
// 如果呼叫 Next 并回傳 false 并且沒有其他結果集,
// 則行將自動關閉,檢查 Err. 的結果就足夠了, 關閉是冪等的,不會影響 Err 的結果,
// 因為不能保證 for rows.Next() 一定可以執行完,有可能會 panic 或其他情況,
// 故需要在此 defer 關閉連接
defer rows.Close()
// 回圈讀取結果集中的資料
// Next 準備下一個結果行,以便使用 Scan 方法讀取,
// 成功時回傳 true,如果沒有下一個結果行或在準備時發生錯誤,則回傳 false,
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
}
// 插入資料
func insertRowDemo(name string, age int) (int64, error) {
sqlStr := "INSERT INTO user (name, age) VALUES (?,?)"
result, err := db.Exec(sqlStr, name, age)
if err != nil {
fmt.Printf("insert failed, err: %v\n", err)
return 0, err
}
// LastInsertId 回傳資料庫為回應命令而生成的整數,
// 通常,這將來自插入新行時的“自動增量”列,
// 并非所有資料庫都支持此功能,并且此類陳述句的語法各不相同,
var newID int64
newID, err = result.LastInsertId() // 新插入資料的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err: %v\n", err)
return 0, err
}
return newID, nil
}
// 更新資料
func updateRowDemo(age, id int) (int64, error) {
sqlStr := "UPDATE user SET age=? WHERE id = ?"
ret, err := db.Exec(sqlStr, age, id)
if err != nil {
fmt.Printf("update failed, err: %v\n", err)
return 0, err
}
// 回傳受更新、插入或洗掉影響的行數,并非每個資料庫或資料庫驅動程式都支持此功能,
var n int64
n, err = ret.RowsAffected() // 操作影響的行數
if err != nil {
fmt.Printf("get RowsAffected failed, err: %v\n", err)
return 0, err
}
return n, nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
// Close 將關閉資料庫并阻止啟動新查詢,關閉,然后等待服務器上已開始處理的所有查詢完成,
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
// 查詢單行資料
//u, err := queryRowDemo(1)
//if err != nil {
// fmt.Printf("query row failed, err: %v", err)
//}
//fmt.Printf("id: %d name: %s age: %d\n", u.id, u.name, u.age)
// 查詢多行資料
//queryMultiRowDemo(0)
// 插入資料
//newID, err := insertRowDemo("小跟班", 12)
//if err != nil {
// fmt.Printf("insert row failed, err: %v", err)
//}
//fmt.Printf("insert success, the id is %d.\n", newID)
// 更新資料
n, err := updateRowDemo(88, 4)
if err != nil {
fmt.Printf("update row failed with err: %v", err)
}
fmt.Printf("update success, affected rows:%d\n", n)
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
update success, affected rows:1
Code/go/mysql_demo via ?? v1.20.3 via ?? base
?
SQL 查詢更新結果
mysql> select * from user;
+----+-----------+------+
| id | name | age |
+----+-----------+------+
| 1 | 小喬 | 16 |
| 2 | 小喬 | 12 |
| 4 | 小跟班 | 88 |
+----+-----------+------+
3 rows in set (0.00 sec)
mysql>
洗掉資料
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql" // 匿名匯入 自動執行 init()
)
var db *sql.DB
func initMySQL() (err error) {
//DSN (Data Source Name)
dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test"
// 注意:要初始化全域的 db 物件,不要新宣告一個 db 變數
db, err = sql.Open("mysql", dsn) // 只對格式進行校驗,并不會真正連接資料庫
if err != nil {
return err
}
// Ping 驗證與資料庫的連接是否仍處于活動狀態,并在必要時建立連接,
err = db.Ping()
if err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
return err
}
// 數值需要根據業務具體情況來確定
db.SetConnMaxLifetime(time.Second * 10) // 設定可以重用連接的最長時間
db.SetConnMaxIdleTime(time.Second * 5) // 設定連接可能處于空閑狀態的最長時間
db.SetMaxOpenConns(200) // 設定與資料庫的最大打開連接數
db.SetMaxIdleConns(10) // 設定空閑連接池中的最大連接數
return nil
}
type user struct {
id int
age int
name string
}
// 查詢單條資料
func queryRowDemo(id int) (user, error) {
sqlStr := "SELECT id, name, age FROM user WHERE id = ?"
var u user
// QueryRow 執行預期最多回傳一行的查詢,
// QueryRow 始侄訓傳一個非 nil 值,錯誤將延遲到呼叫 row 的 Scan 方法,
// Scan 將匹配行中的列復制到 dest 指向的值中
// 如果多行與查詢匹配,Scan 將使用第一行并丟棄其余行,
// 如果沒有與查詢匹配的行,Scan 將回傳 ErrNoRows,
// QueryRow 之后要呼叫 Scan 方法,否則資料庫連接不會被釋放
// Scan 原始碼:defer r.rows.Close()
err := db.QueryRow(sqlStr, id).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err: %v\n", err)
return u, err
}
return u, nil
}
// 查詢多條資料
func queryMultiRowDemo(id int) {
sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
rows, err := db.Query(sqlStr, id)
if err != nil {
fmt.Printf("query failed, err: %v\n", err)
return
}
// 關閉 rows 釋放持有的資料庫鏈接
// Close 關閉行,防止進一步列舉,
// 如果呼叫 Next 并回傳 false 并且沒有其他結果集,
// 則行將自動關閉,檢查 Err. 的結果就足夠了, 關閉是冪等的,不會影響 Err 的結果,
// 因為不能保證 for rows.Next() 一定可以執行完,有可能會 panic 或其他情況,
// 故需要在此 defer 關閉連接
defer rows.Close()
// 回圈讀取結果集中的資料
// Next 準備下一個結果行,以便使用 Scan 方法讀取,
// 成功時回傳 true,如果沒有下一個結果行或在準備時發生錯誤,則回傳 false,
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
}
// 插入資料
func insertRowDemo(name string, age int) (int64, error) {
sqlStr := "INSERT INTO user (name, age) VALUES (?,?)"
result, err := db.Exec(sqlStr, name, age)
if err != nil {
fmt.Printf("insert failed, err: %v\n", err)
return 0, err
}
// LastInsertId 回傳資料庫為回應命令而生成的整數,
// 通常,這將來自插入新行時的“自動增量”列,
// 并非所有資料庫都支持此功能,并且此類陳述句的語法各不相同,
var newID int64
newID, err = result.LastInsertId() // 新插入資料的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err: %v\n", err)
return 0, err
}
return newID, nil
}
// 更新資料
func updateRowDemo(age, id int) (int64, error) {
sqlStr := "UPDATE user SET age=? WHERE id = ?"
ret, err := db.Exec(sqlStr, age, id)
if err != nil {
fmt.Printf("update failed, err: %v\n", err)
return 0, err
}
// 回傳受更新、插入或洗掉影響的行數,并非每個資料庫或資料庫驅動程式都支持此功能,
var n int64
n, err = ret.RowsAffected() // 操作影響的行數
if err != nil {
fmt.Printf("get RowsAffected failed, err: %v\n", err)
return 0, err
}
return n, nil
}
// 洗掉資料
func deleteRowDemo(id int) (int64, error) {
sqlStr := "DELETE FROM user WHERE id = ?"
ret, err := db.Exec(sqlStr, id)
if err != nil {
fmt.Printf("delete failed, err: %v\n", err)
return 0, err
}
// RowsAffected returns the number of rows affected by an
// update, insert, or delete. Not every database or database
// driver may support this.
var n int64
n, err = ret.RowsAffected() // 操作影響的行數
if err != nil {
fmt.Printf("get RowsAffected failed, err: %v\n", err)
return 0, err
}
return n, nil
}
func main() {
if err := initMySQL(); err != nil {
fmt.Printf("connect to db failed, err: %v\n", err)
}
// 檢查完錯誤之后執行,確保 db 不為 nil
// Close() 用來釋放資料庫連接相關的資源
// Close 將關閉資料庫并阻止啟動新查詢,關閉,然后等待服務器上已開始處理的所有查詢完成,
defer db.Close()
fmt.Println("connect to database success")
// db.xx() 去使用資料庫操作...
// 查詢單行資料
//u, err := queryRowDemo(1)
//if err != nil {
// fmt.Printf("query row failed, err: %v", err)
//}
//fmt.Printf("id: %d name: %s age: %d\n", u.id, u.name, u.age)
// 查詢多行資料
//queryMultiRowDemo(0)
// 插入資料
//newID, err := insertRowDemo("小跟班", 12)
//if err != nil {
// fmt.Printf("insert row failed, err: %v", err)
//}
//fmt.Printf("insert success, the id is %d.\n", newID)
// 更新資料
//n, err := updateRowDemo(88, 4)
//if err != nil {
// fmt.Printf("update row failed with err: %v", err)
//}
//fmt.Printf("update success, affected rows: %d\n", n)
// 洗掉資料
n, err := deleteRowDemo(4)
if err != nil {
fmt.Printf("delete row failed with err: %v", err)
}
fmt.Printf("delete success, affected rows:%d\n", n)
}
運行
Code/go/mysql_demo via ?? v1.20.3 via ?? base
? go run main.go
connect to database success
delete success, affected rows:1
Code/go/mysql_demo via ?? v1.20.3 via ?? base
?
SQL 查詢洗掉結果
mysql> select * from user;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 小喬 | 16 |
| 2 | 小喬 | 12 |
+----+--------+------+
2 rows in set (0.00 sec)
mysql>
本文來自博客園,作者:尋月隱君,轉載請注明原文鏈接:https://www.cnblogs.com/QiaoPengjun/p/17472974.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554893.html
標籤:其他
上一篇:Maven常用命令及其作用
下一篇:返回列表
