Never put off what you can do today |

golang连接MySQL数据库

Golang+MySQL

本篇笔记用于记录GoLang连接MySQL数据库以及如何操作MySQL数据库

具体仍有不懂得地方可以参照官方文档 https://github.com/go-sql-driver/mysql

或者李文周大佬的博客 [Go语言操作MySQL | 李文周的博客 (liwenzhou.com)](https://www.liwenzhou.com/posts/Go/go_mysql/)

本文也学自这两处。仅是对自己的学习做一个总结。

Golang连接数据库

预备操作

Go中database/sql包提供了保证SQL或者类SQL数据库的泛用接口,并不提供具体的数据库驱动。所以使用database/sql包去操作数据库的时候必须注入(至少)一个数据库驱动。那么mysql的驱动需要进行下载。

下载依赖go get -u github.com/go-sql-driver/mysql

具体函数操作可以参考go语言中文文档或者官方文档

中文 [Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国 (studygolang.com)](https://studygolang.com/pkgdoc)

官方文档 https://pkg.go.dev/std

首先将连接MySQL所需要的驱动导入,即上文的下载依赖。然后我们为了更好的封装MySQL的操作,我们将操作数据库的对象封装成结构体。

type DbWorker struct {
	Db  *sql.DB // 用于操作数据库
	Dsn string // Data Source Name 用于连接数据库时提供相关信息 
    // 此处字符串的格式:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
    // username:password@protocol(address)/dbname?param=value
}

database/sql包下提供了MySQL驱动,使用下方包中提供的函数可以连接数据库

func Open(driverName, dataSourceName string) (*DB, error)// 此处需要传入的两个参数一个是使用的数据库名称此处我们是"mysql", 以及连接参数dsn

创建数据库以及表

在对数据库进行连接前需要明确进行连接的数据库名以及数据库中的表,此处为了方便举例,我们现在MySQL中创建数据库gostudytest 并在该数据库中创建表user 具体创建语句如下(表中的列以及属性也如下创建语句),若未学习过MySQL或者SQL可以关注blog中的SQL笔记。

CREATE DATABASE gostudytest; -- 创建数据库

USE gostudytest;
CREATE TABLE `user` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(20) DEFAULT '',
    `age` INT(11) DEFAULT '0',
    PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

由于后期需要进行相关增删改查工作,所以需要在数据库中提前加入一些数据,可执行如下语句:

USE gostudytest;

INSERT INTO `user`(name, age)
	VALUES	('张三', 25),
			('王五', 23),
			('解叶', 22)

由此便可以向数据库中加入三个新的行。

从数据库中取出元素的存储

为了方便上面我们创建表中的数据存储以及查询等操作,我们实现声明一个结构体来存储数据

type user struct { // 用于存储从数据库中查询的数据
	id int
	name string
	age int
}

连接数据库的初始化操作

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sql.Open("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	// 上方的连接不会校验账号密码所以不能保证连接成功所以需要ping一下判断是否连接成功
	err = dbw.Db.Ping()
	if err != nil { // 若ping不成功说明连接不成功
		return err
	}
	return nil
}

整体代码:允许即可判断是否连到成功

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type user struct { // 用于存储从数据库中查询的数据
	id   int
	name string
	age  int
}

type DbWorker struct {
	Db  *sql.DB // 用于操作数据库
	Dsn string  // Data Source Name 用于连接数据库时提供相关信息
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sql.Open("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	// 上方的连接不会校验账号密码所以不能保证连接成功所以需要ping一下判断是否连接成功
	err = dbw.Db.Ping()
	if err != nil { // 若ping不成功说明连接不成功
		return err
	}
	return nil
}

func main() {
	db := &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	err := db.InitDb()
	if err != nil {
		fmt.Printf("connect mysql failed: %v\n", err)
		return
	}
	fmt.Println("connect success")
}

Golang针对数据库进行CURD(增删改查)-非预处理

插入、更新、删除操作都使用Exec方法,参数的args是用于替换sql查询语句中的替换符的,若不填写则使用查询语句中默认的

func (db *DB) Exec(query string, args ...any) (Result, error)

返回的Result对象可以调用LastInsertId()方法查看最新插入的id ,调用RowsAffected()方法查询被影响的行数。

具体实现:

func (dbw *DbWorker) InsertData(name string, age int) { // 插入数据
	sqlStr := "INSERT INTO user(name, age) VALUES(?, ?)" // 插入的sql语句
	ret, err := dbw.Db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	newId, err := ret.LastInsertId() //获取最新插入的id
	if err != nil {
		fmt.Printf("get lastinsertid failed: %v\n", err)
		return
	}
	fmt.Printf("Insert sucess ,the id is %v\n", newId)
}

插入、更新、删除操作都使用Exec方法,参数的args是用于替换sql查询语句中的替换符的,若不填写则使用查询语句中默认的

func (db *DB) Exec(query string, args ...any) (Result, error)

返回的Result对象可以调用LastInsertId()方法查看最新插入的id ,调用RowsAffected()方法查询被影响的行数。

具体实现:

func (dbw *DbWorker) DeleteData(id int) { //删除数据
	sqlStr := "DELETE FROM user WHERE id = ?" // 删除的sql语句
	ret, err := dbw.Db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("Delete data failed: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Query RowsAffectd failed: %v\n", err)
		return
	}
	fmt.Printf("Delete Access, affected rows: %v\n", n)
}

插入、更新、删除操作都使用Exec方法,参数的args是用于替换sql查询语句中的替换符的,若不填写则使用查询语句中默认的

func (db *DB) Exec(query string, args ...any) (Result, error)

返回的Result对象可以调用LastInsertId()方法查看最新插入的id ,调用RowsAffected()方法查询被影响的行数。

具体实现:

func (dbw *DbWorker) EditData(age int, id int) { // 修改数据
	sqlStr := "UPDATE user SET age = ? WHERE id = ?" // 修改数据sql语句
	ret, err := dbw.Db.Exec(sqlStr, age, id)
	if err != nil {
		fmt.Printf("Update data failed: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Query RowsAffectd failed: %v\n", err)
		return
	}
	fmt.Printf("Update data success, affected rows: %v\n", n)
}

单行查询

database/sql 包中提供了单行查询的函数QueryRow() 参数的args是用于替换sql查询语句中的替换符的,若不填写则使用查询语句中默认的

func (db *DB) QueryRow(query string, args ...any) *Row

返回一行数据,即一个Row对象。并且该函数总是返回非nil值,直至调用返回值的Scan方法,才会返回错误(如:no rows in result set)

包中Scan方法如下:

func (r *Row) Scan(dest ...any) error 

此处的Scan内部会关闭数据库连接,归还到连接池。

所以我们可以在调用QueryRow后直接调用Scan函数将查询到的数据存储到一个结构体变量中,若未查询到则返回error

具体操作如下:查询编号为id的数据

func (dbw *DbWorker) QueryDataRow(id int) { // 查询单行数据
	var user User
	sqlStr := "SELECT id, name, age FROM user WHERE id = ?" //查询的SQL语句
	err := dbw.Db.QueryRow(sqlStr, id).Scan(&user.id, &user.name, &user.age)
	if err != nil {
		fmt.Println("Scan failed: %v\n", err)
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
}

多行查询

database/sql 包中提供了单行查询的函数Query()参数的args是用于替换sql查询语句中的替换符的,若不填写则使用查询语句中默认的

func (db *DB) Query(query string, args ...any) (*Rows, error)

注意需要再使用后关闭数据库连接否则会使得连接池中的资源耗尽

具体实现:查询编号大于id的数据

func (dbw *DbWorker) QueryDataRows(id int) { // 查询多行数据
	sqlStr := "SELECT id, name, age FROM user WHERE id > ?" //查询的SQL语句
	rows, err := dbw.Db.Query(sqlStr, id)
	defer rows.Close() // 关闭数据库连接所有连接
	if err != nil {
		fmt.Printf("Query Rows failed: %v\n", err)
		return
	}
	fmt.Println("Query Rows success!")
	// 将每一行Scan到结构体变量并输出
	for rows.Next() {
		var user User
		err := rows.Scan(&user.id, &user.name, &user.age)
		if err != nil {
			fmt.Printf("Scan failed: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
	}
}

汇总

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type User struct { // 用于存储从数据库中查询的数据
	id   int
	name string
	age  int
}

type DbWorker struct {
	Db  *sql.DB // 用于操作数据库
	Dsn string  // Data Source Name 用于连接数据库时提供相关信息
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sql.Open("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	// 上方的连接不会校验账号密码所以不能保证连接成功所以需要ping一下判断是否连接成功
	err = dbw.Db.Ping()
	if err != nil { // 若ping不成功说明连接不成功
		return err
	}
	return nil
}
func (dbw *DbWorker) InsertData(name string, age int) { // 插入数据
	sqlStr := "INSERT INTO user(name, age) VALUES(?, ?)" // 插入的sql语句
	ret, err := dbw.Db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	newId, err := ret.LastInsertId() //获取最新插入的id
	if err != nil {
		fmt.Printf("get lastinsertid failed: %v\n", err)
		return
	}
	fmt.Printf("Insert sucess ,the id is %v\n", newId)
}

func (dbw *DbWorker) DeleteData(id int) { //删除数据
	sqlStr := "DELETE FROM user WHERE id = ?" // 删除的sql语句
	ret, err := dbw.Db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("Delete data failed: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Query RowsAffectd failed: %v\n", err)
		return
	}
	fmt.Printf("Delete Access, affected rows: %v\n", n)
}

func (dbw *DbWorker) EditData(age int, id int) { // 修改数据
	sqlStr := "UPDATE user SET age = ? WHERE id = ?" // 修改数据sql语句
	ret, err := dbw.Db.Exec(sqlStr, age, id)
	if err != nil {
		fmt.Printf("Update data failed: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Query RowsAffectd failed: %v\n", err)
		return
	}
	fmt.Printf("Update data success, affected rows: %v\n", n)
}

func (dbw *DbWorker) QueryDataRow(id int) { // 查询单行数据
	var user User
	sqlStr := "SELECT id, name, age FROM user WHERE id = ?" //查询的SQL语句
	err := dbw.Db.QueryRow(sqlStr, id).Scan(&user.id, &user.name, &user.age)
	if err != nil {
		fmt.Println("Scan failed: %v\n", err)
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
}

func (dbw *DbWorker) QueryDataRows(id int) { // 查询多行数据
	sqlStr := "SELECT id, name, age FROM user WHERE id > ?" //查询的SQL语句
	rows, err := dbw.Db.Query(sqlStr, id)
	defer rows.Close() // 关闭数据库连接所有连接
	if err != nil {
		fmt.Printf("Query Rows failed: %v\n", err)
		return
	}
	fmt.Println("Query Rows success!")
	// 将每一行Scan到结构体变量并输出
	for rows.Next() {
		var user User
		err := rows.Scan(&user.id, &user.name, &user.age)
		if err != nil {
			fmt.Printf("Scan failed: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
	}
}

func main() {
	db := &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	err := db.InitDb()
	if err != nil {
		fmt.Printf("connect mysql failed: %v\n", err)
		return
	}
	fmt.Println("connect success")
	//db.QueryDataRow(2)
	//db.QueryDataRows(2)
	//db.InsertData("小太郎", 6)
	//db.DeleteData(1)
	//db.EditData(23, 1)
}

Golang针对数据库进行CURD(增删改查)-预处理

为什么要进行预处理呢?

是因为在多次执行重复的SQL语句时可以优化重复执行过程,一次编译多次执行。

并且可以防止SQL注入问题。

Go实现MySQL预处理

database/sql包下提供了Prepare方法用来实现预处理操作

func (db *DB) Prepare(query string) (*Stmt, error)

Prepare方法会将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的一些操作命令。返回的状态可以执行多条命令。

为了演示预处理可以更好的执行重复操作,所以这里传入一个字典,用于插入多组数据

需要注意管理数据库连接归还至连接池。经过Prepare操作后的插入操作与上文中的查询相似,不做具体说明。

func (dbw *DbWorker) InsertData(record map[string]int) { // 插入数据
	sqlStr := "INSERT INTO user(name, age) VALUES(?, ?)" //预处理操作
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close() 
	cnt := 0
	for k, v := range record {
		ret, err := stmt.Exec(k, v)
		if err != nil {
			fmt.Printf("Exec failed: %v\n", err)
			return
		}
		n, _ := ret.RowsAffected()
		cnt += int(n)
	}
	fmt.Printf("Insert success, there are %d rows affected\n", cnt)
}

为了演示预处理可以更好的执行重复操作,所以这里传入一切片,用于删除多组数据

需要注意管理数据库连接归还至连接池。经过Prepare操作后的删除操作与上文中的查询相似,不做具体说明。

func (dbw *DbWorker) DeleteData(id []int) { //删除数据
	sqlStr := "DELETE FROM user WHERE id = ?"
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	cnt := 0
	for i := range id {
		ret, err := stmt.Exec(id[i])
		if err != nil {
			fmt.Printf("Exec failed: %v\n", err)
			return
		}
		n, _ := ret.RowsAffected()
		cnt += int(n)
	}
	fmt.Printf("Delete success, there are %d rows deleted\n", cnt)
}

func (dbw *DbWorker) EditData(age int, id int) { // 修改数据
	sqlStr := "UPDATE user SET age = ? WHERE id = ?"
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	ret, err := stmt.Exec(age, id)
	if err != nil {
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	n, _ := ret.RowsAffected()
	fmt.Printf("Update success, there are %d rows affected\n", n)
}

需要注意管理数据库连接归还至连接池。经过Prepare操作后的查询操作与上文中的查询相似,不做具体说明。

具体实现:

func (dbw *DbWorker) QueryDataRows(id int) { // 查询数据
	sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
	stmt, err := dbw.Db.Prepare(sqlStr) //预处理操作
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(id) // 查询操作
	if err != nil {
		fmt.Printf("Query failed: %v\n", err)
	}
	fmt.Println("Query success!")
	for rows.Next() {
		var user User
		err := rows.Scan(&user.id, &user.name, &user.age)
		if err != nil {
			fmt.Printf("Scan failed: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
	}
}

汇总

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type User struct { // 用于存储从数据库中查询的数据
	id   int
	name string
	age  int
}

type DbWorker struct {
	Db  *sql.DB // 用于操作数据库
	Dsn string  // Data Source Name 用于连接数据库时提供相关信息
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sql.Open("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	// 上方的连接不会校验账号密码所以不能保证连接成功所以需要ping一下判断是否连接成功
	err = dbw.Db.Ping()
	if err != nil { // 若ping不成功说明连接不成功
		return err
	}
	return nil
}
func (dbw *DbWorker) InsertData(record map[string]int) { // 插入数据
	sqlStr := "INSERT INTO user(name, age) VALUES(?, ?)" //预处理操作
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	cnt := 0
	for k, v := range record {
		ret, err := stmt.Exec(k, v)
		if err != nil {
			fmt.Printf("Exec failed: %v\n", err)
			return
		}
		n, _ := ret.RowsAffected()
		cnt += int(n)
	}
	fmt.Printf("Insert success, there are %d rows affected\n", cnt)
}

func (dbw *DbWorker) DeleteData(id []int) { //删除数据
	sqlStr := "DELETE FROM user WHERE id = ?"
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	cnt := 0
	for i := range id {
		ret, err := stmt.Exec(id[i])
		if err != nil {
			fmt.Printf("Exec failed: %v\n", err)
			return
		}
		n, _ := ret.RowsAffected()
		cnt += int(n)
	}
	fmt.Printf("Delete success, there are %d rows deleted\n", cnt)
}

func (dbw *DbWorker) EditData(age int, id int) { // 修改数据
	sqlStr := "UPDATE user SET age = ? WHERE id = ?"
	stmt, err := dbw.Db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	ret, err := stmt.Exec(age, id)
	if err != nil {
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	n, _ := ret.RowsAffected()
	fmt.Printf("Update success, there are %d rows affected\n", n)
}

func (dbw *DbWorker) QueryDataRows(id int) { // 查询数据
	sqlStr := "SELECT id, name, age FROM user WHERE id > ?"
	stmt, err := dbw.Db.Prepare(sqlStr) //预处理操作
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(id) // 查询操作
	if err != nil {
		fmt.Printf("Query failed: %v\n", err)
	}
	fmt.Println("Query success!")
	for rows.Next() {
		var user User
		err := rows.Scan(&user.id, &user.name, &user.age)
		if err != nil {
			fmt.Printf("Scan failed: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.id, user.name, user.age)
	}
}

func main() {
	db := &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	err := db.InitDb()
	if err != nil {
		fmt.Printf("connect mysql failed: %v\n", err)
		return
	}
	fmt.Println("connect success")
	//db.QueryDataRows(2)
	//record := make(map[string]int)
	//record["蟹蟹"] = 20
	//record["三玖"] = 19
	//db.InsertData(record)
	//a := []int{3, 4, 5}
	//db.DeleteData(a)
	//db.EditData(22, 1)
}

MySQL 事务

MySQL事务的相关定义可以参考:[MySQL 事务 | 菜鸟教程 (runoob.com)](https://www.runoob.com/mysql/mysql-transaction.html)

那么如何使用go实现该操作:

database/sql包下提供了三个方法用于试下MySQL事务

func (db *DB) Begin() (*Tx, error)
func (tx *Tx) Rollback() error
func (tx *Tx) Commit() error

具体实现:

需要注意一个事务当被提交才可以完成进行的所有操作,否则该事务中的所有操作都不能实现。

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

type User struct { // 用于存储从数据库中查询的数据
	id   int
	name string
	age  int
}

type DbWorker struct {
	Db  *sql.DB // 用于操作数据库
	Dsn string  // Data Source Name 用于连接数据库时提供相关信息
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sql.Open("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	// 上方的连接不会校验账号密码所以不能保证连接成功所以需要ping一下判断是否连接成功
	err = dbw.Db.Ping()
	if err != nil { // 若ping不成功说明连接不成功
		return err
	}
	return nil
}

func (dbw *DbWorker) transactionDemo() { //实现一个事务 插入一个数据并且修改该数据
	tx, err := dbw.Db.Begin()
	if err != nil {
		if tx != nil {
			tx.Rollback()
		}
		fmt.Printf("begin transaction failed: %v\n", err)
		return
	}
	sqlStr1 := "INSERT INTO user(name, age) VALUES(?, ?)"
	stmt1, err := tx.Prepare(sqlStr1)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		tx.Rollback()
		return
	}
	ret1, err := stmt1.Exec("四叶", 19)
	if err != nil {
		tx.Rollback()
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	n1, err := ret1.RowsAffected()
	if err != nil {
		tx.Rollback()
		fmt.Printf("get rowsaffected failed: %v\n", err)
		return
	}
	fmt.Printf("%d rows affected\n", n1)
	stmt1.Close()
	sqlStr2 := "UPDATE user SET age = ? WHERE id = ?"
	stmt1, err = dbw.Db.Prepare(sqlStr2)
	if err != nil {
		fmt.Printf("Prepare failed: %v\n", err)
		tx.Rollback()
		return
	}
	ret1, err = stmt1.Exec(19, 18)
	if err != nil {
		tx.Rollback()
		fmt.Printf("Exec failed: %v\n", err)
		return
	}
	n2, err := ret1.RowsAffected()
	if err != nil {
		tx.Rollback()
		fmt.Printf("get rowsaffected failed: %v\n", err)
		return
	}
	fmt.Printf("%d rows affected\n", n2)
	stmt1.Close()
	if n1 == 1 && n2 == 1 {
		fmt.Println("提交事务")
		tx.Commit()
	} else {
		tx.Rollback()
		fmt.Println("事务回滚")
	}
	fmt.Println("transaction success!")
}

func main() {
	db := &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	err := db.InitDb()
	if err != nil {
		fmt.Printf("connect mysql failed: %v\n", err)
		return
	}
	fmt.Println("connect success")
	db.transactionDemo()
}

使用sqlx连接数据库

初始化准备这里直接使用上文中已经建立好的数据库

与database/sql一致,sqlx库也需要使用驱动,所以我么也需要下载上面中已经提及的go get -u github.com/go-sql-driver/mysql

同时我们需要自己下载sqlx库

go get github.com/jmoiron/sqlx

首先我们建立一个用于控制整体数据库操作的结构体,这样可以更好的封装对数据库的操作

type DbWorker struct {
	Db  *sqlx.DB
	Dsn string
}

sqlx包下该函数用于连接数据库,并且该函数与database/sql有所不同的时,在进行连接的同时直接进行了ping操作,由此我们不需要像database/sql库一样自己进行ping操作来判断是否成功连接上数据库。

此处也可以使用MustConnect,该函数当连接不成功直接panic

func Connect(driverName, dataSourceName string) (*DB, error)
func MustConnect(driverName, dataSourceName string) *DB

连接数据库初始化操作

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sqlx.Connect("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	dbw.Db.SetMaxOpenConns(20)
	dbw.Db.SetMaxIdleConns(10)
	return nil
}

整体代码:

package main

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

type User struct {
	Id   int    `db:"id"`
	Name string `db:"name"`
	Age  int    `db:"age"`
}

type DbWorker struct {
	Db  *sqlx.DB
	Dsn string
}

var Dbw *DbWorker

func main() {
	Dbw = &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	if err := Dbw.InitDb(); err != nil {
		fmt.Printf("connect mysql failed, error: %v\n", err)
		return
	}
	fmt.Println("connect mysql success!")
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sqlx.Connect("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	dbw.Db.SetMaxOpenConns(20)
	dbw.Db.SetMaxIdleConns(10)
	return nil
}

sqlx提供如下函数实现查询单行数据的功能

func (db *DB) Get(dest interface{}, query string, args ...interface{}) error

注意由于此处可以直接通过传入一个实体的指针,将查询所得到的数据直接存入该实体(由反射实现),所以需要将结构体中的字段大写,并且由于字段大写了,但实际查询所得出的与sql语句中的不符合,所以需要对结构体中的字段进行label绑定。

具体实现:

func (dbw *DbWorker) QueryRowData(id int) {
	sqlStr := `SELECT * FROM user WHERE id = ?`
	var user User
	err := dbw.Db.Get(&user, sqlStr, 1)
	if err != nil {
		fmt.Printf("Query row data failed, error: %v\n")
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
}

sqlx中提供了如下函数实现多行数据的查询

func (db *DB) Select(dest interface{}, query string, args ...interface{}) error

此处的select方法可以将查询到的数据直接存入到对应的位置(dest),相较于database/sqlQuery会更加的方便。

具体实现:

func (dbw *DbWorker) QueryRowsData(id int) {
	sqlStr := `SELECT id, name, age FROM user WHERE id > ?`
	var users []User
	err := dbw.Db.Select(&users, sqlStr, 2)
	if err != nil {
		fmt.Printf("Query rows failed, error: %v\n", err)
		return
	}
	fmt.Println("Query Success!")
	for _, u := range users {
		fmt.Printf("id: %v  name: %v  age: %v\n", u.Id, u.Name, u.Age)
	}
}

总体实现

由于增删改于database/sql类似,所以在此不做赘述,仅提供完整实现。

package main

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

type User struct {
	Id   int    `db:"id"`
	Name string `db:"name"`
	Age  int    `db:"age"`
}

type DbWorker struct {
	Db  *sqlx.DB
	Dsn string
}

var Dbw *DbWorker

func main() {
	Dbw = &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	if err := Dbw.InitDb(); err != nil {
		fmt.Printf("connect mysql failed, error: %v\n", err)
		return
	}
	fmt.Println("connect mysql success!")
	// Dbw.QueryRowData(1)
	// Dbw.QueryRowsData(2)
	// Dbw.InsertData("张三条", 20)
	// Dbw.DeleteData(13)
	Dbw.UpdateData(20, 1)
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sqlx.Connect("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	dbw.Db.SetMaxOpenConns(20)
	dbw.Db.SetMaxIdleConns(10)
	return nil
}

func (dbw *DbWorker) InsertData(name string, age int) {
	sqlStr := `INSERT INTO user(name, age) VALUES(?, ?)`
	ret, err := dbw.Db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("Insert values failed, error: %v\n", err)
		return
	}
	theid, err := ret.LastInsertId()
	if err != nil {
		fmt.Printf("Get the new line id failed, error: %v\n", err)
		return
	}
	fmt.Printf("Insert success! the new line's id is %v\n", theid)
}

func (dbw *DbWorker) DeleteData(id int) {
	sqlStr := `DELETE FROM user WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("Delete data failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Delete success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) UpdateData(age int, id int) {
	sqlStr := `UPDATE user SET age = ? WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, age, id)
	if err != nil {
		fmt.Printf("Update failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Update success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) QueryRowData(id int) {
	sqlStr := `SELECT * FROM user WHERE id = ?`
	var user User
	err := dbw.Db.Get(&user, sqlStr, 1)
	if err != nil {
		fmt.Printf("Query row data failed, error: %v\n")
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
}

func (dbw *DbWorker) QueryRowsData(id int) {
	sqlStr := `SELECT id, name, age FROM user WHERE id > ?`
	var users []User
	err := dbw.Db.Select(&users, sqlStr, 2)
	if err != nil {
		fmt.Printf("Query rows failed, error: %v\n", err)
		return
	}
	fmt.Println("Query Success!")
	for _, u := range users {
		fmt.Printf("id: %v  name: %v  age: %v\n", u.Id, u.Name, u.Age)
	}
}

NamedExec()

当需要大量使用占位符来插入数据或者更新数据时,可以使用NamedExec()

func (tx *Tx) NamedExec(query string, arg interface{}) (sql.Result, error)

NamedExec()第二个参数传入的是一个map,而map得key则是占位符(需要加上:)

并且NamedExec查询在一个事务中,任何命名占位符参数从参数字段获取

func (dbw *DbWorker) InsertUser() {
	sqlStr := `INSERT INTO user(name, age) VALUES(:name, :age)`
	_, err := dbw.Db.NamedExec(sqlStr, map[string]interface{}{
		"name": "王小二",
		"age":  20,
	})
	if err != nil {
		fmt.Printf("Insert failed (use NamedExec), error: %v\n", err)
		return
	}
	fmt.Println("Insert success(use NamedExec)")
}

NamedQuery()

NamedQuery()函数可以通过map(key值)或struct(字段绑定的label)去替换占位符参数,实现相应操作,再通过ExecStructScan等函数将查询到的行存储到结构体中。

func (tx *Tx) NamedQuery(query string, arg interface{}) (*Rows, error)
func (r *Rows) StructScan(dest interface{}) error
func (dbw *DbWorker) NamedQueryDemo() {
	sqlStr := `SELECT * FROM user WHERE name = :name`
	rows, err := dbw.Db.NamedQuery(sqlStr, map[string]interface{}{
		"name": "王小二",
	})
	if err != nil {
		fmt.Printf("NamedQuery failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}

	u := User{
		Name: "智乃",
	}
	rows, err = dbw.Db.NamedQuery(sqlStr, u)
	if err != nil {
		fmt.Printf("NamedQuery(struct) failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}
}

整体实现

package main

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

type User struct {
	Id   int    `db:"id"`
	Name string `db:"name"`
	Age  int    `db:"age"`
}

type DbWorker struct {
	Db  *sqlx.DB
	Dsn string
}

var Dbw *DbWorker

func main() {
	Dbw = &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	if err := Dbw.InitDb(); err != nil {
		fmt.Printf("connect mysql failed, error: %v\n", err)
		return
	}
	fmt.Println("connect mysql success!")
	// Dbw.QueryRowData(1)
	// Dbw.QueryRowsData(2)
	// Dbw.InsertData("张三条", 20)
	// Dbw.DeleteData(13)
	// Dbw.UpdateData(20, 1)
	// Dbw.InsertUser()
	Dbw.NamedQueryDemo()
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sqlx.Connect("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	dbw.Db.SetMaxOpenConns(20)
	dbw.Db.SetMaxIdleConns(10)
	return nil
}

func (dbw *DbWorker) InsertUser() {
	sqlStr := `INSERT INTO user(name, age) VALUES(:name, :age)`
	_, err := dbw.Db.NamedExec(sqlStr, map[string]interface{}{
		"name": "王小二",
		"age":  20,
	})
	if err != nil {
		fmt.Printf("Insert failed (use NamedExec), error: %v\n", err)
		return
	}
	fmt.Println("Insert success(use NamedExec)")
}

func (dbw *DbWorker) InsertData(name string, age int) {
	sqlStr := `INSERT INTO user(name, age) VALUES(?, ?)`
	ret, err := dbw.Db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("Insert values failed, error: %v\n", err)
		return
	}
	theid, err := ret.LastInsertId()
	if err != nil {
		fmt.Printf("Get the new line id failed, error: %v\n", err)
		return
	}
	fmt.Printf("Insert success! the new line's id is %v\n", theid)
}

func (dbw *DbWorker) DeleteData(id int) {
	sqlStr := `DELETE FROM user WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("Delete data failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Delete success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) NamedQueryDemo() {
	sqlStr := `SELECT * FROM user WHERE name = :name`
	rows, err := dbw.Db.NamedQuery(sqlStr, map[string]interface{}{
		"name": "王小二",
	})
	if err != nil {
		fmt.Printf("NamedQuery failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}

	u := User{
		Name: "智乃",
	}
	rows, err = dbw.Db.NamedQuery(sqlStr, u)
	if err != nil {
		fmt.Printf("NamedQuery(struct) failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}
}

func (dbw *DbWorker) UpdateData(age int, id int) {
	sqlStr := `UPDATE user SET age = ? WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, age, id)
	if err != nil {
		fmt.Printf("Update failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Update success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) QueryRowData(id int) {
	sqlStr := `SELECT * FROM user WHERE id = ?`
	var user User
	err := dbw.Db.Get(&user, sqlStr, 1)
	if err != nil {
		fmt.Printf("Query row data failed, error: %v\n")
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
}

func (dbw *DbWorker) QueryRowsData(id int) {
	sqlStr := `SELECT id, name, age FROM user WHERE id > ?`
	var users []User
	err := dbw.Db.Select(&users, sqlStr, 2)
	if err != nil {
		fmt.Printf("Query rows failed, error: %v\n", err)
		return
	}
	fmt.Println("Query Success!")
	for _, u := range users {
		fmt.Printf("id: %v  name: %v  age: %v\n", u.Id, u.Name, u.Age)
	}
}

sqlx实现事务

sqlx中由以下函数实现事务的处理

func (db *DB) Beginx() (*Tx, error)
tx.Commit()
……

并且通过defer实现错误处理

defer func() {
		if p := recover(); p != nil { //使用recover当发生panic的时候从新获得程序掌握全,并且使得事务回滚
			tx.Rollback()
			panic(p)
		} else if err != nil {
			fmt.Println("rollback")
			tx.Rollback()
		} else {
			err = tx.Commit() // 提交事务
			fmt.Println("commit")
		}
	}()
func (dbw *DbWorker) Transaction() (err error) {
	tx, err := dbw.Db.Beginx()
	if err != nil {
		fmt.Printf("begin transaction failed, error: %v\n", err)
		return err
	}
	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		} else if err != nil {
			fmt.Println("rollback")
			tx.Rollback()
		} else {
			err = tx.Commit()
			fmt.Println("commit")
		}
	}()
	sqlStr := `Update user SET age = 12 WHERE id = ?`
	res, err := tx.Exec(sqlStr, 9)
	if err != nil {
		return err
	}
	n, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return errors.New("exec sqlStr failed")
	}
	sqlStr = `UPDATE user SET age = 30 WHERE id = ?`
	res, err = tx.Exec(sqlStr, 14)
	if err != nil {
		return err
	}
	n, err = res.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return errors.New("exec sqlstr failed")
	}
	return err
}

sqlx.In()

当我们自己实现批量插入的时候,需要进行如下的字符串拼接以及相关操作

func (dbw *DbWorker) BatchInsertUser(users []*User) error {
	// 存放(?, ?)
	valueStrings := make([]string, 0, len(users))
	// 存放需要插入的数据的slice
	valueArgs := make([]interface{}, 0, len(users)*2)

	for _, v := range users {
		valueStrings = append(valueStrings, "(?, ?)")
		valueArgs = append(valueArgs, v.Name)
		valueArgs = append(valueArgs, v.Age)
	}
	stmt := fmt.Sprintf("INSERT INTO user(name, age) VALUES %s", strings.Join(valueStrings, ","))
	_, err := dbw.Db.Exec(stmt, valueArgs...)
	return err
}

当使用sqlx.In()的时候,我们需要针对存储数据的结构体实现一个driver.Valuer接口:

func (u User) Value() (driver.Value, error) {
	return []interface{}{u.Name, u.Age}, nil
}

具体实现:

func (dbw *DbWorker) BatchInsertUserIn(users []interface{}) error {
	// args实现了Value(),sqlx.IN()会调用Value来将其展开并且放入到?的位置
	query, args, err := sqlx.In("INSERT INTO user (name, age) VALUES (?), (?)", users...)
	if err != nil {
		fmt.Printf("sqlx.In() failed, error: %v\n", err)
		return err
	}
	fmt.Println(query)
	fmt.Println(args)
	_, err = dbw.Db.Exec(query, args...)
	return err
}

使用NamedExec()实现批量插入

func (dbw *DbWorker) BatchInsertUserNamedExec(users []*User) error {
	_, err := dbw.Db.NamedExec("INSERT INTO user(name, age) VALUES(:name, :age)", users)
	return err
}

使用sqlx.In()查询指定字段的结果

查询id字段的结果

func (dbw *DbWorker) QueryByIDs(ids []int) (users []User, err error) {
	query, args, err := sqlx.In("SELECT id, name, age FROM user WHERE id IN (?)", ids)
	if err != nil {
		return
	}
	query = dbw.Db.Rebind(query)
	fmt.Println(query)
	err = dbw.Db.Select(&users, query, args...)
	return
}

按照指定顺序进行查询并返回结果

func (dbw *DbWorker) QueryAndOrderByid(ids []int) (users []User, err error) {
	strIds := make([]string, 0, len(ids))
	for _, id := range ids {
		strIds = append(strIds, fmt.Sprintf("%d", id))
	}
	query, args, err := sqlx.In("SELECT name, age FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)", ids, strings.Join(strIds, ","))
	if err != nil {
		return
	}
	query = dbw.Db.Rebind(query)
	fmt.Println(query)
	err = dbw.Db.Select(&users, query, args...)
	return
}

全部实现

package main

import (
	"database/sql/driver"
	"errors"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"strings"
)

type User struct {
	Id   int    `db:"id"`
	Name string `db:"name"`
	Age  int    `db:"age"`
}

type DbWorker struct {
	Db  *sqlx.DB
	Dsn string
}

var Dbw *DbWorker

func main() {
	Dbw = &DbWorker{
		Dsn: "root:@tcp(localhost:3306)/gostudytest?charset=utf8mb4",
	}
	if err := Dbw.InitDb(); err != nil {
		fmt.Printf("connect mysql failed, error: %v\n", err)
		return
	}
	fmt.Println("connect mysql success!")
	//Dbw.QueryRowData(1)
	// Dbw.QueryRowsData(2)
	// Dbw.InsertData("张三条", 20)
	// Dbw.DeleteData(13)
	// Dbw.UpdateData(20, 1)
	// Dbw.InsertUser()
	// Dbw.NamedQueryDemo()
	// Dbw.Transaction()
	// users := []*User{
	// 	&User{Name: "一条", Age: 20},
	// 	&User{Name: "二条", Age: 19},
	// }
	// err := Dbw.BatchInsertUser(users)
	// if err != nil {
	// 	fmt.Printf("Insert failed, error: err", err)
	// 	return
	// }
	// fmt.Println("Insert success")
	// users := []interface{}{
	// 	User{Name: "一花", Age: 20},
	// 	User{Name: "五月", Age: 19},
	// }
	// err := Dbw.BatchInsertUserIn(users)
	// if err != nil {
	// 	fmt.Printf("Insert failed, error: err", err)
	// 	return
	// }
	// fmt.Println("Insert success")
	// users := []*User{
	// 	&User{Name: "卡卡西", Age: 20},
	// 	&User{Name: "鸣人", Age: 19},
	// }
	// Dbw.BatchInsertUserNamedExec(users)
	// users, err := Dbw.QueryByIDs([]int{1, 42, 15})
	// if err != nil {
	// 	fmt.Printf("Query failed, error: %v\n", err)
	// 	return
	// }
	// for _, v := range users {
	// 	fmt.Println(v.Id, v.Name, v.Age)
	// }
	users, err := Dbw.QueryByIDs([]int{1, 42, 15})
	if err != nil {
		fmt.Printf("Query failed, error: %v\n", err)
		return
	}
	for _, v := range users {
		fmt.Println(v.Id, v.Name, v.Age)
	}
}

func (u User) Value() (driver.Value, error) {
	return []interface{}{u.Name, u.Age}, nil
}

func (dbw *DbWorker) InitDb() (err error) {
	dbw.Db, err = sqlx.Connect("mysql", dbw.Dsn)
	if err != nil {
		return err
	}
	dbw.Db.SetMaxOpenConns(20)
	dbw.Db.SetMaxIdleConns(10)
	return nil
}

func (dbw *DbWorker) InsertUser() {
	sqlStr := `INSERT INTO user(name, age) VALUES(:name, :age)`
	_, err := dbw.Db.NamedExec(sqlStr, map[string]interface{}{
		"name": "王小二",
		"age":  20,
	})
	if err != nil {
		fmt.Printf("Insert failed (use NamedExec), error: %v\n", err)
		return
	}
	fmt.Println("Insert success(use NamedExec)")
}

func (dbw *DbWorker) InsertData(name string, age int) {
	sqlStr := `INSERT INTO user(name, age) VALUES(?, ?)`
	ret, err := dbw.Db.Exec(sqlStr, name, age)
	if err != nil {
		fmt.Printf("Insert values failed, error: %v\n", err)
		return
	}
	theid, err := ret.LastInsertId()
	if err != nil {
		fmt.Printf("Get the new line id failed, error: %v\n", err)
		return
	}
	fmt.Printf("Insert success! the new line's id is %v\n", theid)
}

func (dbw *DbWorker) DeleteData(id int) {
	sqlStr := `DELETE FROM user WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("Delete data failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Delete success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) NamedQueryDemo() {
	sqlStr := `SELECT * FROM user WHERE name = :name`
	rows, err := dbw.Db.NamedQuery(sqlStr, map[string]interface{}{
		"name": "王小二",
	})
	if err != nil {
		fmt.Printf("NamedQuery failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}

	u := User{
		Name: "智乃",
	}
	rows, err = dbw.Db.NamedQuery(sqlStr, u)
	if err != nil {
		fmt.Printf("NamedQuery(struct) failed, error: %v\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var user User
		err := rows.StructScan(&user)
		if err != nil {
			fmt.Printf("Scan failed, error: %v\n", err)
			return
		}
		fmt.Printf("id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
	}
}

func (dbw *DbWorker) UpdateData(age int, id int) {
	sqlStr := `UPDATE user SET age = ? WHERE id = ?`
	ret, err := dbw.Db.Exec(sqlStr, age, id)
	if err != nil {
		fmt.Printf("Update failed, error: %v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("Get affected row failed, error: %v\n", err)
		return
	}
	fmt.Printf("Update success, there are %v rows affected\n", n)
}

func (dbw *DbWorker) QueryRowData(id int) {
	sqlStr := `SELECT * FROM user WHERE id = ?`
	var user User
	err := dbw.Db.Get(&user, sqlStr, 1)
	if err != nil {
		fmt.Printf("Query row data failed, error: %v\n")
		return
	}
	fmt.Printf("Query success! id: %v  name: %v  age: %v\n", user.Id, user.Name, user.Age)
}

func (dbw *DbWorker) QueryRowsData(id int) {
	sqlStr := `SELECT id, name, age FROM user WHERE id > ?`
	var users []User
	err := dbw.Db.Select(&users, sqlStr, 2)
	if err != nil {
		fmt.Printf("Query rows failed, error: %v\n", err)
		return
	}
	fmt.Println("Query Success!")
	for _, u := range users {
		fmt.Printf("id: %v  name: %v  age: %v\n", u.Id, u.Name, u.Age)
	}
}

func (dbw *DbWorker) Transaction() (err error) {
	tx, err := dbw.Db.Beginx()
	if err != nil {
		fmt.Printf("begin transaction failed, error: %v\n", err)
		return err
	}
	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		} else if err != nil {
			fmt.Println("rollback")
			tx.Rollback()
		} else {
			err = tx.Commit()
			fmt.Println("commit")
		}
	}()
	sqlStr := `Update user SET age = 12 WHERE id = ?`
	res, err := tx.Exec(sqlStr, 9)
	if err != nil {
		return err
	}
	n, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return errors.New("exec sqlStr failed")
	}
	sqlStr = `UPDATE user SET age = 30 WHERE id = ?`
	res, err = tx.Exec(sqlStr, 14)
	if err != nil {
		return err
	}
	n, err = res.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return errors.New("exec sqlstr failed")
	}
	return err
}

func (dbw *DbWorker) BatchInsertUser(users []*User) error {
	// 存放(?, ?)
	valueStrings := make([]string, 0, len(users))
	// 存放需要插入的数据的slice
	valueArgs := make([]interface{}, 0, len(users)*2)

	for _, v := range users {
		valueStrings = append(valueStrings, "(?, ?)")
		valueArgs = append(valueArgs, v.Name)
		valueArgs = append(valueArgs, v.Age)
	}
	stmt := fmt.Sprintf("INSERT INTO user(name, age) VALUES %s", strings.Join(valueStrings, ","))
	_, err := dbw.Db.Exec(stmt, valueArgs...)
	return err
}

func (dbw *DbWorker) BatchInsertUserIn(users []interface{}) error {
	// args实现了Value(),sqlx.IN()会调用Value来将其展开并且放入到?的位置
	query, args, err := sqlx.In("INSERT INTO user (name, age) VALUES (?), (?)", users...)
	if err != nil {
		fmt.Printf("sqlx.In() failed, error: %v\n", err)
		return err
	}
	fmt.Println(query)
	fmt.Println(args)
	_, err = dbw.Db.Exec(query, args...)
	return err
}

func (dbw *DbWorker) BatchInsertUserNamedExec(users []*User) error {
	_, err := dbw.Db.NamedExec("INSERT INTO user(name, age) VALUES(:name, :age)", users)
	return err
}

func (dbw *DbWorker) QueryByIDs(ids []int) (users []User, err error) {
	query, args, err := sqlx.In("SELECT id, name, age FROM user WHERE id IN (?)", ids)
	if err != nil {
		return
	}
	query = dbw.Db.Rebind(query)
	fmt.Println(query)
	err = dbw.Db.Select(&users, query, args...)
	return
}

func (dbw *DbWorker) QueryAndOrderByid(ids []int) (users []User, err error) {
	strIds := make([]string, 0, len(ids))
	for _, id := range ids {
		strIds = append(strIds, fmt.Sprintf("%d", id))
	}
	query, args, err := sqlx.In("SELECT name, age FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)", ids, strings.Join(strIds, ","))
	if err != nil {
		return
	}
	query = dbw.Db.Rebind(query)
	fmt.Println(query)
	err = dbw.Db.Select(&users, query, args...)
	return
}

本文作者:“账号已注销”

本文链接:https://www.cnblogs.com/nil-xjh/p/16101739.html

版权声明:本作品采用xjh许可协议进行许可。

posted @   “账号已注销”  阅读(103)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 memento TK from 凛として時雨
memento - TK from 凛として時雨
00:00 / 00:00
An audio error has occurred.

作词 : TK

作曲 : TK

ビルの窓に反射している東京タワーに少し何かが近づいたような気がして

ビルの窓に反射している東京タワーに少し何かが近づいたような気がして

誰かの悩みも聞けるからさそれっぽい答え方も出来るようになったんだ

捨て去ったもので記憶は溢れていて小さくなった心を

捨て去ったもので記憶は溢れていて小さくなった心を

これが大人なんだって誤魔化すのも上手くなった

さよならの伝え方だけは僕の中を通り過ぎて

さよならの伝え方だけは僕の中を通り過ぎて

寂しそうな笑顔だけがなんか上手く消せないから

またどこかで会えたらいいね

またどこかで会えたらいいね

誰もいないかくれんぼの様さ

会えもしないこの街の中で

探して探して探して

隠したのは僕だ

隠したのは僕だ

不安定な脳内で君に会わない様に

またどこかでまたどこかで

倒れかかった僕が依りかからない様に

壊したのは僕だ

壊したのは僕だ

曖昧な言葉で密室の中に逃げ道を創った

大切なものが見えていた君が僕には怖くて

I was the only one たった1人の

I was the only one たった1人の

I was the only one 1人だった

I was the only one 忘れていいよ

すれ違う人混み溢れかえるニュース置き去りになって

すれ違う人混み溢れかえるニュース置き去りになって

窓に映る僕は1人都会の渦の中で僕は1人

僕は君の何を壊したんだろう隠したんだ

無くなったものが片目に溢れて

無くなったものが片目に溢れて

手に入れたものが片目に溢れて

滲んでしまった滲んでしまった

瞼を閉じて溢れた記憶が街のノイズに

飲み込まれて何もかもが離れていくんだ僕は1人

I was the only one たった1人の

I was the only one たった1人の

I was the only one 1人にさえも

なれなかったならなかった?

僕はただの1人になって残された