关于 Go 的标准库 database/sql 和 sqlx

database/sql 是 Go 操作数据库的标准库之一,它提供了一系列接口方法,用于访问数据库(mysql,sqllite,oralce,postgresql),它并不会提供数据库特有的方法,那些特有的方法交给数据库驱动去实现

而通常在工作中,我们更多的是用  https://github.com/jmoiron/sqlx 包来操作数据库,sqlx 是基于标准库 sql 的扩展,并且我们可以通过 sqlx 操作各种类型的数据,如将查询的数据转为结构体等

github 地址:

  • https://github.com/go-sql-driver/mysql
  • https://github.com/jmoiron/sqlx

安装:

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

 

sqlx 库提供了一些类型,掌握这些类型的用法非常的重要

1)DB(数据库对象)

sql.DB 类型代表了数据库,其它语言操作数据库的时候,需要创建一个连接,对于 Go 而言则是需要创建一个数据库类型,它不是数据库连接,Go 中的连接来自内部实现的连接池,连接的建立是惰性的,连接将会在操作的时候,由连接池创建并维护

使用 sql.Open 函数创建数据库类型,第一个是数据库驱动名,第二个是连接信息的字符串

var Db *sqlx.DB
db, err := sqlx.Open("mysql","username:password@tcp(ip:port)/database?charset=utf8")
Db = db

 

2)Results 和 Result(结果集)

新增、更新、删除;和查询所用的方法不一样,所有返回的类型也不同

  • Result 是 新增、更新、删除时返回的结果集
  • Results 是查询数据库时的结果集,sql.Rows 类型表示查询返回多行数据的结果集,sql.Row 则表示单行查询的结果集

 

3)Statements(语句)

sql.Stmt 类型表示 sql 语句,例如 DDL,DML 等类似的 sql 语句,可以当成 prepare 语句构造查询,也可以直接使用 sql.DB 的函数对其操作

 

实践部分(数据库CURD)

数据库建表

以下所有 demo 都以下表结构作为基础

CREATE TABLE `userinfo` (
    `uid` INT(10) NOT NULL AUTO_INCREMENT,
    `create_time` datetime DEFAULT NULL,
    `username` VARCHAR(64)  DEFAULT NULL,
    `password` VARCHAR(32)  DEFAULT NULL,
    `department` VARCHAR(64)  DEFAULT NULL,
    `email` varchar(64) DEFAULT NULL,
    PRIMARY KEY (`uid`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

Exec() 方法使用(新增、修改、删除)

func (db *DB) Exec(query string, args ...interface{}) (Result, error)

Exec 和 MustExec 从连接池中获取一个连接然后指向对应的 query 操作,对于不支持 ad-hoc query execution 的驱动,在操作执行的背后会创建一个 prepared statement,在结果返回前,这个 connection 会返回到连接池中

需要注意的是,不同的数据库,使用的占位符不同,mysql 采用 ? 作为占位符

  • Mysql 使用 ?
  • PostgreSQL 使用 1,1,2 等等
  • SQLLite 使用 ? 或 $1
  • Oracle 使用 :name        (注意有冒号)

 

demo:定义了 4 个函数,分别是 连接数据库,插入数据,更新数据,删除数据

关于 下面数据库操作的几个小知识点

  1. 插入数据后可以通过 LastInsertId() 方法获取插入数据的主键 id
  2. 通过 RowsAffected 可以获取受影响的行数
  3. 通过 Exec() 方法插入数据,返回的结果是 sql.Result 类型
package main

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

var (
	userName  string = "chenkai"
	password  string = "chenkai"
	ipAddrees string = "192.168.0.115"
	port      int    = 3306
	dbName    string = "test"
	charset   string = "utf8"
)

func connectMysql() (*sqlx.DB) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)
	Db, err := sqlx.Open("mysql", dsn)
	if err != nil {
		fmt.Printf("mysql connect failed, detail is [%v]", err.Error())
	}
	return Db
}

func addRecord(Db *sqlx.DB) {
	for i:=0; i<2; i++ {
		result, err := Db.Exec("insert into userinfo  values(?,?,?,?,?,?)",0, "2019-07-06 11:45:20", "johny", "123456", "技术部", "123456@163.com")
		if err != nil {
			fmt.Printf("data insert faied, error:[%v]", err.Error())
			return
		}
		id, _ := result.LastInsertId()
		fmt.Printf("insert success, last id:[%d]\n", id)
	}
}

func updateRecord(Db *sqlx.DB){
	//更新uid=1的username
	result, err := Db.Exec("update userinfo set username = 'anson' where uid = 1")
	if err != nil {
		fmt.Printf("update faied, error:[%v]", err.Error())
		return
	}
	num, _ := result.RowsAffected()
	fmt.Printf("update success, affected rows:[%d]\n", num)
}

func deleteRecord(Db *sqlx.DB){
	//删除uid=2的数据
	result, err := Db.Exec("delete from userinfo where uid = 2")
	if err != nil {
		fmt.Printf("delete faied, error:[%v]", err.Error())
		return
	}
	num, _ := result.RowsAffected()
	fmt.Printf("delete success, affected rows:[%d]\n", num)
}


func main() {
	var Db *sqlx.DB = connectMysql()
	defer Db.Close()

	addRecord(Db)
	updateRecord(Db)
	deleteRecord(Db)
}

运行结果:
API server listening at: 127.0.0.1:59899
insert success, last id:[1]
insert success, last id:[2]
update success, affected rows:[1]
delete success, affected rows:[1]

  

Query() 方法使用(查询单个字段数据)

func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

Query() 方法返回的是一个 sql.Rows 类型的结果集

也可以用来查询多个字段的数据,不过需要定义多个字段的变量进行接收

迭代后者的 Next() 方法,然后使用 Scan() 方法给对应类型变量赋值,以便取出结果,最后再把结果集关闭(释放连接)

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


var (
	userName  string = "chenkai"
	password  string = "chenkai"
	ipAddrees string = "192.168.0.115"
	port      int    = 3306
	dbName    string = "test"
	charset   string = "utf8"
)

func connectMysql() (*sqlx.DB) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)
	Db, err := sqlx.Open("mysql", dsn)
	if err != nil {
		fmt.Printf("mysql connect failed, detail is [%v]", err.Error())
	}
	return Db
}

func queryData(Db *sqlx.DB) {
	rows, err := Db.Query("select * from userinfo")
	if err != nil {
		fmt.Printf("query faied, error:[%v]", err.Error())
		return
	}
	for rows.Next() {
		//定义变量接收查询数据
		var uid int
		var create_time, username, password, department, email string

		err := rows.Scan(&uid, &create_time, &username, &password, &department, &email)
		if err != nil {
			fmt.Println("get data failed, error:[%v]", err.Error())
		}
		fmt.Println(uid, create_time, username, password, department, email)
	}
	
	//关闭结果集(释放连接)
	rows.Close()
}

func main() {
	var Db *sqlx.DB = connectMysql()
	defer Db.Close()

	queryData(Db)
}

运行结果:
1 2019-07-06 11:45:20 anson 123456 技术部 123456@163.com
3 2019-07-06 11:45:20 johny 123456 技术部 123456@163.com
4 2019-07-06 11:45:20 johny 123456 技术部 123456@163.com

 

Get() 方法使用

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

是将查询到的一条记录,保存到结构体

结构体的字段名首字母必须大写,不然无法寻址

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

var (
    userName  string = "chenkai"
    password  string = "chenkai"
    ipAddrees string = "192.168.0.115"
    port      int    = 3306
    dbName    string = "test"
    charset   string = "utf8"
)

func connectMysql() (*sqlx.DB) {
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", userName, password, ipAddrees, port, dbName, charset)
    Db, err := sqlx.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("mysql connect failed, detail is [%v]", err.Error())
    }
    return Db
}

func getData(Db *sqlx.DB) {
    type userInfo struct {
        Uid int `db:"uid"`
        UserName string `db:"username"`
        CreateTime string `db:"create_time"`
        Password string `db:"password"`
        Department string `db:"department"`
        Email string `db:"email"`
    }

    //初始化定义结构体,用来存放查询数据
    var userData *userInfo = new(userInfo)
    err := Db.Get(userData,"select *from userinfo where uid = 1")
    if err != nil {
        fmt.Printf("query faied, error:[%v]", err.Error())
        return
    }

    //打印结构体内容
    fmt.Println(userData.Uid, userData.CreateTime, userData.UserName,
        userData.Password, userData.Department, userData.Email)
}

func main() {
    var Db *sqlx.DB = connectMysql()
    defer Db.Close()

    getData(Db)
}

运行结果:
1 2019-07-06 11:45:20 anson 123456 技术部 123456@163.com
View Code

相关文章:

  • 2021-10-03
  • 2022-12-23
  • 2022-12-23
  • 2022-03-09
  • 2022-12-23
  • 2022-12-23
  • 2021-08-08
猜你喜欢
  • 2021-09-29
  • 2021-12-21
  • 2022-12-23
相关资源
相似解决方案