Go语言详解:实现MySQL数据库的增删改查操作一、初始化连接创建项目配置Environmenthttps://goproxy.cn,directMySQL数据库驱动MySQL驱动https://github.com/go-sql-driver/mysqlgoget-ug
https://goproxy.cn,direct
MySQL驱动<https://github.com/go-sql-driver/mysql>
go get -u github.com/go-sql-driver/mysql
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
➜
// 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
}
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
➜
// 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 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)
}
}
~ 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
➜
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
➜
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
➜
mysql> select * from user;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 小乔 | 16 |
| 2 | 小乔 | 12 |
+----+--------+------+
2 rows in set (0.00 sec)
mysql>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!