Go 语言之 sqlx 库使用

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

Go 语言之 sqlx 库使用

一、sqlx 库安装与连接

sqlx 介绍

sqlx is a library which provides a set of extensions on go's standard database/sql library. The sqlx versions of sql.DB, sql.TX, sql.Stmt, et al. all leave the underlying interfaces untouched, so that their interfaces are a superset on the standard ones. This makes it relatively painless to integrate existing codebases using database/sql with sqlx.

Sqlx 是一个库,它在 go 的标准数据库/sql 库上提供了一组扩展。

sqlx库:https://github.com/jmoiron/sqlx

Illustrated guide to SQLX:http://jmoiron.github.io/sqlx/

sqlx:https://pkg.go.dev/github.com/jmoiron/sqlx

安装

go get github.com/jmoiron/sqlx

创建 sqlx_demo 项目

https://raw.githubusercontent.com/qiaopengjun5162/blogpicgo/master/img202306112235131.png

创建 main.go 文件

https://raw.githubusercontent.com/qiaopengjun5162/blogpicgo/master/img202306112239546.png

使用 go mod tidy 添加丢失的模块和删除未使用的模块

go mod tidy add missing and remove unused modules

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go mod tidy   
go: finding module for package github.com/jmoiron/sqlx
go: finding module for package github.com/go-sql-driver/mysql
go: downloading github.com/jmoiron/sqlx v1.3.5
go: found github.com/go-sql-driver/mysql in github.com/go-sql-driver/mysql v1.7.1
go: found github.com/jmoiron/sqlx in github.com/jmoiron/sqlx v1.3.5
go: downloading github.com/lib/pq v1.2.0
go: downloading github.com/mattn/go-sqlite3 v1.14.6

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

连接 MySQL 数据库

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base took 2.0s 
➜ 

二、sqlx 的 CRUD (查询、插入、更新、修改)

查询单条数据

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝

// 查询单条数据
func queryRowDemo(id int) (u user, err error) {
	sqlStr := "SELECT id, name, age FROM user WHERE id=?"
	// 使用这个数据库。任何占位符参数都将被提供的参数替换。如果结果集为空,则返回错误。
  // 在 Get 中要修改传入的变量,需要传指针,即 &u
	err = db.Get(&u, sqlStr, id)
	if err != nil {
		fmt.Printf("get failed, err:%v\n", err)
		return u, err
	}
	return u, nil
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
	// 查询
	u, err := queryRowDemo(1)
	if err != nil {
		fmt.Printf("query row demo failed, err %v\n", err)
	}
	fmt.Printf("id: %d name: %s age: %d\n", u.ID, u.Name, u.Age)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
id: 1 name: 小乔 age: 16

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base took 3.1s 
➜ 

查询多条数据

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝

// 查询多条数据
func queryMultiRowDemo(id int) (users []user, err error) {
	sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
	err = db.Select(&users, sqlStr, id)
	if err != nil {
		fmt.Printf("query failed, err: %v\n", err)
		return users, err
	}
	return users, nil
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")

	// 查询多条
	users, err := queryMultiRowDemo(0)
	if err != nil {
		fmt.Printf("query many rows failed %v\n", err)
	}
	fmt.Printf("users: %#v\n", users)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base took 3.1s 
➜ go run main.go
init DB succeeded
users: []main.user{main.user{ID:1, Age:16, Name:"小乔"}, main.user{ID:2, Age:22, Name:"小乔"}, main.user{ID:5, Age:100, Name:"昭君"}, main.user{ID:6, Age:16, Name:"黛玉"}}

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

~ via 🅒 base
➜ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 476
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> 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> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |  100 |
|  6 | 黛玉   |   16 |
+----+--------+------+
4 rows in set (0.00 sec)

mysql>

插入

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝

// 插入数据
func insertRowDemo(name string, age int) (int64, error) {
	sqlStr := "INSERT INTO user(name, age) VALUES (?,?)"
	// 执行查询而不返回任何行。
	// 参数用于查询中的任何占位符参数
	ret, err := db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("insert failed, err: %v\n", err)
		return 0, err
	}
	// 返回数据库响应命令生成的整数。
	// 通常,当插入新行时,这将来自“自动增量”列。
	// 并非所有数据库都支持此特性,并且此类语句的语法各不相同。
	var LastInsertId int64
	LastInsertId, err = ret.LastInsertId() // 新插入数据的id
	if err != nil {
		fmt.Printf("get lastinsert ID failed, err: %v\n", err)
		return 0, err
	}
	return LastInsertId, nil
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")

	// 插入数据
	LastInsertId, err := insertRowDemo("宝玉", 17)
	if err != nil {
		fmt.Printf("insert row demo failed %v\n", err)
	}
	fmt.Printf("insert success, the id is %d.\n", LastInsertId)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
insert success, the id is 7.

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |  100 |
|  6 | 黛玉   |   16 |
|  7 | 宝玉   |   17 |
+----+--------+------+
5 rows in set (0.01 sec)

mysql>

更新

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝


// 更新数据
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 := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")

	// 更新数据
	n, err := updateRowDemo(18, 5)
	if err != nil {
		fmt.Printf("update row demo failed %v\n", err)
	}
	fmt.Printf("update success, affected rows: %d\n", n)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
update success, affected rows: 1

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  7 | 宝玉   |   17 |
+----+--------+------+
5 rows in set (0.00 sec)

mysql>

删除

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝

// 删除数据
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
	}
	// 返回受更新、插入或删除影响的行数。并非每个数据库或数据库驱动程序都支持此功能。
	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 := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
  
	// 删除数据
	n, err := deleteRowDemo(7)
	if err != nil {
		fmt.Printf("delete row demo failed %v\n", err)
	}
	fmt.Printf("delete success, affected rows:%d\n", n)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
delete success, affected rows:1

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  7 | 宝玉   |   17 |
+----+--------+------+
5 rows in set (0.00 sec)

mysql>

NamedExec 的使用

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

// 1. 想要让别的包能够访问到结构体中的字段,那这个结构体中的字段需要首字母大写
// 2. Go 语言中参数的传递是值拷贝


func insertUserDemo(arg interface{}) (int64, error) {
	sqlStr := "INSERT INTO user (name,age) VALUES (:name,:age)"
	// 使用这个数据库。任何命名的占位符参数都将被arg中的字段替换。
	Result, err := db.NamedExec(sqlStr, arg)
	if err != nil {
		return 0, err
	}
	// 返回数据库响应命令生成的整数。
	// 通常,当插入新行时,这将来自“自动增量”列。
	// 并非所有数据库都支持此特性,并且此类语句的语法各不相同。
	var new_id int64
	new_id, err = Result.LastInsertId() // 新插入数据的id
	if err != nil {
		fmt.Printf("get lastinsert ID failed, err: %v\n", err)
		return 0, err
	}
	return new_id, nil
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
	
	// NamedExec
	arg := map[string]interface{}{"name": "李煜", "age": 26}
	new_id, err := insertUserDemo(arg)
	if err != nil {
		fmt.Printf("insert user demo failed %v\n", err)
	}
	fmt.Printf("insert user success, the new id is %d\n", new_id)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
insert user success, the new id is 8

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  8 | 李煜   |   26 |
+----+--------+------+
5 rows in set (0.01 sec)

mysql>

NamedQuery 的使用

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

func namedQueryMap(arg interface{}) {
	sqlStr := "SELECT * FROM user WHERE name=:name"
	// 任何命名的占位符参数都将被arg中的字段替换。
	rows, err := db.NamedQuery(sqlStr, arg)
	if err != nil {
		fmt.Printf("db.NamedQuery failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		results := make(map[string]interface{})
		// 使用 map 做命名查询
		err := rows.MapScan(results)
		//dest, err := rows.SliceScan()
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			continue
		}
		// 将 "name" 字段的值转换为字符串类型
		if nameBytes, ok := results["name"].([]uint8); ok {
			results["name"] = string(nameBytes)
		}
		fmt.Printf("NamedQuery Map user: %#v\n", results)
	}
}

func namedQuerySlice(arg interface{}) {
	sqlStr := "SELECT * FROM user WHERE name=:name"
	// 任何命名的占位符参数都将被arg中的字段替换。
	rows, err := db.NamedQuery(sqlStr, arg)
	if err != nil {
		fmt.Printf("db.NamedQuery failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		results, err := rows.SliceScan()
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			continue
		}
		if len(results) >= 3 {
			id, _ := results[0].(int64)
			name, _ := results[1].([]uint8)
			age, _ := results[2].(int64)

			fmt.Printf("NamedQuery Slice user: id=%d, name=%s, age=%d\n", id, string(name), age)
		}
	}
}

func namedQueryStruct(arg interface{}) {
	sqlStr := "SELECT * FROM user WHERE name=:name"
	// 任何命名的占位符参数都将被arg中的字段替换。
	rows, err := db.NamedQuery(sqlStr, arg)
	if err != nil {
		fmt.Printf("db.NamedQuery failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var results user
		err := rows.StructScan(&results)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			continue
		}
		fmt.Printf("NamedQuery struct user: %#v\n", results)
	}
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
	
	// NamedQuery
	// 使用 map 做命名查询
	arg := map[string]interface{}{"name": "黛玉"}
	namedQueryMap(arg)
	// 使用结构体命名查询,根据结构体字段的 db tag进行映射
	arg1 := user{Name: "黛玉"}
	namedQueryStruct(arg1)
	// 使用 Slice 做命名查询
	arg2 := []user{arg1}
	namedQuerySlice(arg2)
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
NamedQuery Map user: map[string]interface {}{"age":16, "id":6, "name":"黛玉"}
NamedQuery struct user: main.user{ID:6, Age:16, Name:"黛玉"}
NamedQuery Slice user: id=6, name=黛玉, age=16

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果文章来源地址https://www.toymoban.com/news/detail-481040.html

mysql> select * from user;
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   16 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  8 | 李煜   |   26 |
+----+--------+------+
5 rows in set (0.00 sec)

mysql>

sqlx 事务

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:12345678@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 连接到数据库并使用ping进行验证。
	// 也可以使用 MustConnect MustConnect连接到数据库,并在出现错误时恐慌 panic。
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20) // 设置数据库的最大打开连接数。
	db.SetMaxIdleConns(10) // 设置空闲连接池中的最大连接数。
	return
}

type user struct {
	ID   int    `db:"id"`
	Age  int    `db:"age"`
	Name string `db:"name"`
}

func executeQuery(tx *sqlx.Tx, sqlStr string, args ...interface{}) error {
	rs, err := tx.Exec(sqlStr, args...)
	if err != nil {
		return err
	}

	// 返回受更新、插入或删除影响的行数。并非每个数据库或数据库驱动程序都支持此功能。
	var n int64
	n, err = rs.RowsAffected()
	if err != nil {
		return err
	}

	if n != 1 { // 如果受影响的行数不是 1 ,说明更新出了问题,return 错误
		return fmt.Errorf("exec failed for id: %d", args[1])
	}

	return nil
}

func transactionDemo() error {
	// 开始事务并返回sqlx.Tx而不是sql.Tx。
	tx, err := db.Beginx() // 开启事务
	if err != nil {
		return fmt.Errorf("begin trans failed, err: %v", err)
	}

	defer func() {
		// recover 恢复内置功能允许程序管理一个恐慌的程序的行为。
		// recover的返回值报告了例程是否处于恐慌状态。
    // recover 捕获当前函数可能会出现的 panic,执行恢复操作,即先回滚然后 panic
		if p := recover(); p != nil {
			_ = tx.Rollback() // 中止回滚事务。
			panic(p)
		} else if err != nil { // 判断 err 是否为空,如果当前函数有错误,则回滚
			fmt.Printf("begin trans failed, Rollback err: %v\n", err)
			_ = tx.Rollback()
		} else { // 如果没有错误也没有 panic 则提交
			err = tx.Commit()
			fmt.Printf("error committing transaction: %v\n", err)
		}
	}()

	sqlStr := "UPDATE user SET age=? WHERE id=?"

	queries := []struct {
		id  int
		age int
	}{
		{1, 88},
		{3, 16},
	}
	
  // 更新用户表 user 中两个用户的年龄,只有当这两个用户的年龄都更新成功的情况下才会去提交
	for _, query := range queries {
		if err = executeQuery(tx, sqlStr, query.age, query.id); err != nil {
			return err
		}
	}

	return nil
}

func main() {
	if err := initDB(); err != nil {
		fmt.Printf("init DB failed, err:%v\n", err)
		return
	}
	fmt.Println("init DB succeeded")
	
	// 事务
	_ = transactionDemo()
}

运行

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ go run main.go
init DB succeeded
begin trans failed, Rollback err: exec failed for id: 3

Code/go/sqlx_demo via 🐹 v1.20.3 via 🅒 base 
➜ 

SQL 查询结果

mysql> select * from user;  # 事务执行前
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   12 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  8 | 李煜   |   26 |
+----+--------+------+
5 rows in set (0.00 sec)

mysql> select * from user;  # 事务执行后
+----+--------+------+
| id | name   | age  |
+----+--------+------+
|  1 | 小乔   |   12 |
|  2 | 小乔   |   22 |
|  5 | 昭君   |   18 |
|  6 | 黛玉   |   16 |
|  8 | 李煜   |   26 |
+----+--------+------+
5 rows in set (0.01 sec)

mysql>
  • 使用 defer 来做事务最终是回滚还是提交的判断。
  • 执行之后,事务回滚了,查询数据库未发生改变,是因为 ID 为 3 的那条记录不存在。

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

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

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

相关文章

  • Mouse without Borders(无界鼠标)使用教程 多台电脑(最多4)共用鼠标键盘,换言之一套键鼠操作多台电脑,跨电脑文件拖动传输

    目的: 多台电脑(最多4)共用鼠标键盘,换言之一套键鼠操作多台电脑。 优势: 微软官方软件,对于windows系统友好,轻量实用。 劣势: windows友好,但是mac等其他系统不能用或者兼容性不强(未实测)。有其他系统的PC需要实现类似功能,推荐尝试ShareMouse软件,或者用罗

    2024年03月25日
    浏览(221)
  • 【Go 基础篇】Go语言结构体基本使用

    在Go语言中,结构体是一种重要的数据类型,用于定义和组织一组不同类型的数据字段。结构体允许开发者创建自定义的复合数据类型,类似于其他编程语言中的类。本文将深入探讨Go语言中结构体的定义、初始化、嵌套、方法以及与其他语言的对比,以便全面了解如何在Go中

    2024年02月10日
    浏览(42)
  • Go语言之 go-redis 基本使用

    Redis:https://redis.io/ Redis 中文网:https://www.redis.net.cn/ REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 它通常被称为

    2024年02月09日
    浏览(38)
  • 言之画: AI绘画平台

    【产品介绍】   言之画是出门问问推出的AI绘画平台。支持二次元、蒸汽朋克、插画等 8 种创作风格。用户只需输入文字,「言之画」就能一次性生成 8 张光影逼真、细节丰富的 2K 高分辨率图像。   除了以文生图,它还拥有以图生图、动图生成、个性头像生成等 AI 绘图能

    2024年02月04日
    浏览(50)
  • 使用Go 语言的三个原因

    几个星期前,我一个朋友问我:“为什么要关心 Go 语言”? 因为他们知道我热衷于 Go 语言,但他们想知道为什么我认为其他人也应该关心。有三个原因:安全性、生产力和并发性。有些语言可以涵盖一个也有可能是两个方面,但是这三个方面的结合使得 Go 成为主流程序员的

    2024年02月13日
    浏览(50)
  • Go语言中使用kafka

    Windows环境下安装zookeeper和kafka

    2024年02月07日
    浏览(46)
  • 使用Go语言进行安卓开发

    本文将介绍如何使用Go语言进行安卓开发。我们将探讨使用Go语言进行安卓开发的优点、准备工作、基本概念和示例代码。通过本文的学习,你将了解如何使用Go语言构建高效的安卓应用程序。 随着移动互联网的快速发展,安卓应用程序的需求越来越旺盛。使用传统的Java和K

    2024年02月06日
    浏览(46)
  • go语言中前端模板的使用

    一、配置直接加载静态目录 1、项目中创建一个文件夹为 assets 来存放静态目录(图片、 css 、 js ) 2、在 main.go 文件中添加以下代码 3、直接在浏览器上输入 http://localhost:8080/assets/1122.jpg 就可以访问静态目录的文件 4、如果在 gin 中使用呢 二、模板渲染 1、在项目中创建一个 vie

    2024年02月08日
    浏览(37)
  • 使用Go语言实现HTTPS请求

    Go语言,又称Golang,是一种高效、简洁的编程语言。它内置了对HTTP和HTTPS的支持,使得在Go中实现HTTPS请求变得非常简单。下面是一个简单的示例,展示了如何使用Go发送HTTPS请求。 首先,确保你已经安装了Go语言环境。然后,创建一个新的Go文件,比如 https_request.go 。 在 http

    2024年01月18日
    浏览(42)
  • 使用Go语言搭建区块链基础

    随着区块链技术的发展,越来越多的人开始关注并使用这一技术,其中,比特币和以太坊等区块链项目正在成为人们关注的焦点。而Go语言作为一种高效、简洁的编程语言,越来越多的区块链项目也选择使用Go语言来搭建其底层基础。本文将介绍如何使用Go语言搭建区块链的基

    2024年02月04日
    浏览(36)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包