Go 语言连接数据库实现增删改查

这篇具有很好参考价值的文章主要介绍了Go 语言连接数据库实现增删改查。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

title: "Go 语言连接数据库实现增删改查"
date: 2023-06-10T18:55:16+08:00
draft: true
tags: ["Go"]
categories: ["Go"]

Go 连接 MySQL实现增删改查

一、初始化连接

创建项目

配置 Environment

https://goproxy.cn,direct

MySQL 数据库驱动

MySQL驱动https://github.com/go-sql-driver/mysql文章来源地址https://www.toymoban.com/news/detail-478975.html

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>

到了这里,关于Go 语言连接数据库实现增删改查的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • pycharm社区版使用SQLite连接数据库,并实现数据的增删改查

    社区版找不到数据库,需要先安装Database Navigator插件,之后才能通过sqlite3连接数据库。 ①文件 — ②设置 — ③插件 — ④Marketplace搜索database — ⑤安装Database Navigator — ⑥应用确定 安装之后就可以在页面左侧边栏找到DB Browser,也可以拖动移动到页面右侧。找不到的可以在视

    2024年01月17日
    浏览(52)
  • Vue项目通过node连接MySQL数据库并实现增删改查操作

    1.创建Vue项目 Vue项目创建的详细步骤,有需要的可移步这里 2.下载安装需要的插件 下载express 下载cors,用于处理接口跨域问题 下载mysql 下载axios 3.在项目中创建server文件夹,用于搭建本地服务器 新建/server/app.js,用于配置服务器相关信息 新建/server/db/index.js,用于配置数据库

    2024年02月16日
    浏览(64)
  • Java实现neo4j数据库连接及增删改查

    天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。 可参考文章: neo4j数据库的介绍及基础语法 neo4j数据库语法实例演示 在增删改查之前我

    2024年02月14日
    浏览(42)
  • Go 语言实现 MySQL 数据库事务

    MySQL事务是指一组数据库操作,它们被视为一个逻辑单元,并且要么全部成功执行,要么全部回滚(撤销)。事务是数据库管理系统提供的一种机制,用于确保数据的一致性和完整性。 事务具有以下特性(通常由ACID原则定义): 原子性(Atomicity):事务中的所有操作要么全

    2024年02月08日
    浏览(47)
  • 从零实现一个数据库(DataBase) Go语言实现版 0.介绍

    英文源地址 我们为什么需要数据库?为什么不是直接把数据dump进文件中. 第一个话题就是持久化. 我们将讨论如果写入文件的过程中程序崩溃了, 或者电源断电了, 文件的状态会是什么样的呢? 文件是否只是丢失了最后一次写操作? 或者以写了一半的文件结束 或者是以更差的状态

    2024年02月06日
    浏览(39)
  • golang 连接 oracle 数据库 增删改查

     1,golang 连接 oracle 数据库  2,增删改查

    2024年02月09日
    浏览(50)
  • Python之数据库操作(连接数据库,增删改查操作,易错点理解)

    文章目录 前言 一、Python之数据库操作 二、 pymysql 安装 三、pymysql 包引入  连接数据库 创建游标 执行sql数据 - 增删改查 要获取查询结果数据 关闭游标,关闭数据库连接 总结 记录:Python操作数据库的步骤,不容易理解的地方。 学习地址: python与各大数据库的连接: http:/

    2023年04月16日
    浏览(57)
  • django连接本地数据库并执行增删改查

    models.py文件根据数据库表映射出对应的类 在views.py中实现增删改查操作: 增: 删: 改: 查:

    2024年02月20日
    浏览(44)
  • Java/JavaWeb连接数据库完成增删改查(胎教级教程)

    目录 项目展示:(增删改查)环境:Tomcat 8.5 1.数据库结构         1.1 创建数据库(source_db)         1.2 创建数据表(tb_source),结构如下。 2.项目文件结构 3.jar包导入 4.创建JDBC数据库访问类:JDBCutil 5.创建实体类:Source 6.创建数据访问层:SourceDao 7.创建业务逻辑层

    2024年02月08日
    浏览(37)
  • 【PHP系统学习】——Laravel框架数据库的连接以及数据库的增删改查的详细教程

    👨‍💻个人主页 :@开发者-曼亿点 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 曼亿点 原创 👨‍💻 收录于专栏 :PHP程序开发 —   按照 MVC 的架构,对数据的操作应该放在 Model 中完成,但如果不使用 Model,我们也可以用 laravel框架提供的 D8 类

    2024年04月15日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包