Go语言中的MySQL数据库操作技巧

发表时间: 2024-05-14 21:33

Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能:

sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作.

sql.DB 为我们管理数据库连接池

需要注意的是, sql.DB表示操作数据库的抽象访问接口, 而非一个数据库连接对象; 它可以根据driver打开关闭数据库连接, 管理连接池。

正在使用的连接被标记为繁忙, 用完后回到连接池等待下次使用。所以, 如果你没有把连接释放回连接池, 会导致过多连接使系统资源耗尽。

go 第三方插件实现增删改查

go-sql-driver

1)下载

go get github.com/go-sql-driver/mysql

在$GOPATH项目地址内执行

2)导入依赖包

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

3) 连接数据库

db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/changwai?parseTime=true")
DNS格式: [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]

完整的代码:

package mainimport (    "database/sql"    "log"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/changwai?parseTime=true")    if err != nil {    log.Fatalln(err)    }    defer db.Close()}

sql.Open的第一个参数是driver名称, 第二个参数是driver连接数据库的信息, 各个driver可能不同。

DB不是连接, 并且只有当需要使用时才会创建连接, 如果想立即验证连接, 需要用Ping()方法, 如下

err = db.Ping()if err != nil {// do something here}

查询语句

CREATE TABLE `tw_user` (    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,    `username` varchar(50) NOT NULL,    `mobile` varchar(50) DEFAULT NULL,    `mobiletime` int(11) unsigned NOT NULL DEFAULT '0',    `password` varchar(32) NOT NULL,) ENGINE=InnoDB AUTO_INCREMENT=6297 DEFAULT CHARSET=utf8 COMMENT='用户信息表';

读取DB

如果方法包含Query, 那么这个方法是用于查询并返回rows的。其他情况应该用Exec()。

package mainimport (    "database/sql"    "fmt"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3307)/btcbing_com?parseTime=true")    if err != nil {        fmt.Println(err)    }    defer db.Close()    var (        id int        name string    )    rows, err := db.Query("select id, username from tw_user where id = ?", 6273) //查询的是单行    //rows, err := db.Query("select id, username from tw_user where id > 6273") //查询的是多行    if err != nil {        fmt.Println(err)    }    defer rows.Close()    for rows.Next() {    err := rows.Scan(&id, &name)    if err != nil {        fmt.Println(err)    }    fmt.Println(id, name)    }    err = rows.Err()    if err != nil {        fmt.Println(err)    }}

上面代码的过程为:db.Query()表示向数据库发送一个query, defer rows.Close()非常重要, 遍历rows使用rows.Next(), 把遍历到的数据存入变量使用rows.Scan(), 遍历完成后检查error。

有几点需要注意:

检查遍历是否有error

结果集(rows)未关闭前, 底层的连接处于繁忙状态。当遍历读到最后一条记录时, 会发生一个内部EOF错误, 自动调用rows.Close(), 但是如果提前退出循环, rows不会关闭, 连接不会回到连接池中, 连接也不会关闭。

所以手动关闭非常重要。rows.Close()可以多次调用, 是无害操作。

QueryRow()方法

err在Scan后才产生, 所以可以如下写:

package mainimport (    "database/sql"    "fmt"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3307)/btcbing_com?parseTime=true")    if err != nil {        fmt.Println(err)    }    defer db.Close()    var username string    err = db.QueryRow("select username from tw_user where id = ?", 6273).Scan(&username)    if err != nil {        fmt.Println(err)    }    fmt.Println(username)}

Query 和 QueryRow 两者都是查询的方法, 返回的结果比较

Query *Rows, errorQueryRow *Row


type Row struct {    err error    rows *Rows}

实例: 预准备语句(增删改查)

CREATE TABLE `userinfo` (    `uid` INT(10) NOT NULL AUTO_INCREMENT,    `username` VARCHAR(64) NULL DEFAULT NULL,    `departname` VARCHAR(64) NULL DEFAULT NULL,    `created` DATE NULL DEFAULT NULL,    PRIMARY KEY (`uid`))
CREATE TABLE `userdetail` (    `uid` INT(10) NOT NULL DEFAULT '0',    `intro` TEXT NULL,    `profile` TEXT NULL,    PRIMARY KEY (`uid`))
package mainimport (    _ "github.com/go-sql-driver/mysql"    "database/sql"    "fmt"    //"time")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3307)/btcbing_com?parseTime=true")    checkErr(err)    //插入数据    stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")    checkErr(err)    res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")    checkErr(err)    id, err := res.LastInsertId()    checkErr(err)    fmt.Println(id)    //更新数据    stmt, err = db.Prepare("update userinfo set username=? where uid=?")    checkErr(err)    res, err = stmt.Exec("astaxieupdate", id)    checkErr(err)    affect, err := res.RowsAffected()    checkErr(err)    fmt.Println(affect)    //查询数据    rows, err := db.Query("SELECT * FROM userinfo")    checkErr(err)    for rows.Next() {    var uid int    var username string    var department string    var created string    err = rows.Scan(&uid, &username, &department, &created)    checkErr(err)    fmt.Println(uid)    fmt.Println(username)    fmt.Println(department)    fmt.Println(created)    }    //删除数据    stmt, err = db.Prepare("delete from userinfo where uid=?")    checkErr(err)    res, err = stmt.Exec(id)    checkErr(err)    affect, err = res.RowsAffected()    checkErr(err)    fmt.Println(affect)    db.Close()}func checkErr(err error) {    if err != nil {    	panic(err)    }}

修改数据, 事务

一般用Prepare()和Exec()完成INSERT, UPDATE, DELETE操作。

package mainimport (    "database/sql"    "fmt"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3307)/btcbing_com?parseTime=true")    if err != nil {        fmt.Println(err)    }    stmt, err := db.Prepare("INSERT INTO userinfo(username) VALUES(?)")    if err != nil {        fmt.Println(err)    }    res, err := stmt.Exec("Dolly")    if err != nil {        fmt.Println(err)    }    lastId, err := res.LastInsertId() //获取最后自动增长值    if err != nil {        fmt.Println(err)    }    rowCnt, err := res.RowsAffected() //获取影响的行数    if err != nil {        fmt.Println(err)    }    fmt.Printf("ID = %d, affected = %d\n", lastId, rowCnt)}

事务

db.Begin()开始事务, Commit() 或 Rollback()关闭事务。

如果你需要通过多条语句修改连接状态, 你必须使用Tx,例如:

package mainimport (    "database/sql"    "fmt"    "time"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3307)/btcbing_com?parseTime=true")    if err != nil {    fmt.Println(err)    }    tx, err := db.Begin() //开始事务    if err != nil {        fmt.Println(err)    }    defer tx.Rollback() //回滚    stmt, err := tx.Prepare("INSERT INTO userinfo VALUES (?, ?, ?, ?)")    if err != nil {        fmt.Println(err)    }    defer stmt.Close() // danger!    _, err = stmt.Exec(4, "xuchenkai", "财务部", time.Now().Format("2006-01-02")) //执行语句    if err != nil {        fmt.Println(err)    }    err = tx.Commit() //提交事务    if err != nil {        fmt.Println(err)    }}// stmt.Close() runs here!