#|Go语言标准库学习之database/sql——数据库管理的利器

在Go语言标准库中提供了进行数据库操作的 database/sql 库,需要注意的是在使用 sql 库的时候需要导入数据库驱动。本文记录了 database/sql 标准库的学习笔记,希望对你有帮助。
1. 数据库连接 链接数据库只需要四步即可:
生成连接语句 导入数据库引擎 连接数据库 设置连接参数 测试链接
  • 导入数据库引擎
    下面列出了常用的数据库引擎,更过请参考 数据库引擎列表
    github.com/go-sql-driver/mysql/ github.com/mattn/go-sqlite3

  • 连接数据库
    连接数据库前需要设置数据库连接参数,如下例:
    type MySqlConfig struct { UserName string Password string IPstring Portint Database string }func parseMysqlConfig(m MySqlConfig) string { return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", m.UserName, m.Password, m.IP, m.Port, m.Database) }

    设置完参数,我们通过Open函数连接数据库:
    db, err := sql.Open("mysql", parseMysqlConfig(mysql)) if err != nil { panic(err) }

  • 设置连接参数
    设置的参数有三种:
    // 设置连接的生命周期,超过这个时间他会被Close,一般不会设置这个 db.SetConnMaxLifetime(time.Hour * 24) // 设置最大空闲连接池的大小,默认为2 db.SetMaxIdleConns(100) // 设置最大连接数量 db.SetMaxOpenConns(20)

  • 测试链接
    db 有一个 ping 方法用来测试是否连接成功:
    // 通过 ping 测试数据库是否连接成功 if err := db.Ping(); err != nil { panic(err) }

我们看一下完整代码:
package mainimport ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" )type MySqlConfig struct { UserName string Password string IPstring Portint Database string }func parseMysqlConfig(m MySqlConfig) string { return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", m.UserName, m.Password, m.IP, m.Port, m.Database) }func ConnectMysql() (*sql.DB, error) { // Mysql 数据库配置 mysql := MySqlConfig{ UserName: "root", Password: "root", IP:"127.0.0.1", Port:3306, Database: "test", } db, err := sql.Open("mysql", parseMysqlConfig(mysql)) if err != nil { return db, err } // 设置连接的生命周期,超过这个时间他会被Close,一般不会设置这个 db.SetConnMaxLifetime(time.Hour * 24) // 设置最大空闲连接池的大小,默认为2 db.SetMaxIdleConns(100) // 设置最大连接数量 db.SetMaxOpenConns(20) // 通过 ping 测试数据库是否连接成功 if err := db.Ping(); err != nil { return db, err } fmt.Println("Connect Mysql Success....") return db, nil }

调用 ConnectMysql 函数,输出Connect Mysql Success....表示连接成功。
2. 执行 SQL 语句
  • 执行一条 Sql 命令
    // Exec执行一次命令(包括查询、删除、更新、插入等),不返回任何执行结果。 // 参数args表示query中的占位参数。 // MySQL 占位符为 ? // PostgreSQL 占位符为 $1, $2等 // SQLite 占位符为 ? 和$1 // Oracle 占位符为 :name func (db *DB) Exec(query string, args ...interface{}) (Result, error)

  • 执行一次查询返回多条结果
    // Query执行一次查询,返回多行结果(即Rows),一般用于执行select命令。 // 参数args表示query中的占位参数。 func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

  • 执行一次查询期望返回最多一条结果
    // QueryRow执行一次查询,并期望返回最多一行结果(即Row)。 // QueryRow总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。(如:未找到结果) func (db *DB) QueryRow(query string, args ...interface{}) *Row

  • Prepare
    // Prepare创建一个准备好的状态用于之后的查询和命令。 // 返回值可以同时执行多个查询和命令。 func (db *DB) Prepare(query string) (*Stmt, error)

接下来我们使用上面的方法进行数据库的修改与查询:
func main() { db, _ := ConnectMysql() // 插入数据 if _, err := db.Exec("INSERT INTO student (name,age,school,sex) VALUES (?, ?, ?, ?) ", "random1", 20, "school1", 1); err != nil { fmt.Println(err) return } // 使用Query查询数据 rows, err := db.Query("SELECT * FROM student") if err != nil { fmt.Println(err) return } fmt.Println("使用Query:\n", "ID", "|", "Name", "|", "Age", "|", "School", "|", "Sex") for rows.Next() { var ( IDint Namestring Ageint School string Sexint ) if err := rows.Scan(&ID, &Name, &Age, &School, &Sex); err != nil { fmt.Println(err) return } fmt.Println(ID, Name, Age, School, Sex) } // 使用 var ( IDint Namestring Ageint School string Sexint ) _ = db.QueryRow("SELECT * FROM student WHERE id = 3").Scan(&ID, &Name, &Age, &School, &Sex) fmt.Println("使用QueryRow:\n", ID, Name, Age, School, Sex) }

Output:
$ go run main.go Connect Mysql Success.... 使用Query: ID | Name | Age | School | Sex 1 random 20 school1 1 3 random1 20 school1 1 使用QueryRow: 3 random1 20 school1 1

3. 获取 Sql 执行结果
  • Row ( Row 只有一个方法,用来保存结果到指定的变量 )
    // Scan将该行查询结果各列分别保存进dest参数指定的值中。 // 如果该查询匹配多行,Scan会使用第一行结果并丢弃其余各行。如果没有匹配查询的行,Scan会返回ErrNoRows。 func (r *Row) Scan(dest ...interface{}) error

  • Rows
    // Columns返回列名。如果Rows已经关闭会返回错误。 func (rs *Rows) Columns() ([]string, error) // Scan将当前行各列结果填充进dest指定的各个值中。 func (rs *Rows) Scan(dest ...interface{}) error // Next准备用于Scan方法的下一行结果。 // 如果成功会返回真,如果没有下一行或者出现错误会返回假。 // Err应该被调用以区分这两种情况。 func (rs *Rows) Next() bool // Close关闭Rows,阻止对其更多的列举。 func (rs *Rows) Close() error // Err返回可能的、在迭代时出现的错误。Err需在显式或隐式调用Close方法后调用。 func (rs *Rows) Err() error

4. Stmt Stmt是准备好的状态。Stmt可以安全的被多个go程同时使用,db.Prepare函数返回与当前连接相关的执行 Sql 语句的准备状态,可以进行查询、删除等操作。
// Exec使用提供的参数执行准备好的命令状态,返回Result类型的该状态执行结果的总结。 func (s *Stmt) Exec(args ...interface{}) (Result, error) // Query使用提供的参数执行准备好的查询状态,返回Rows类型查询结果。 func (s *Stmt) Query(args ...interface{}) (*Rows, error) // QueryRow使用提供的参数执行准备好的查询状态。 func (s *Stmt) QueryRow(args ...interface{}) *Row // Close关闭状态。 func (s *Stmt) Close() error

举个例子:
func main(){ db, _ := ConnectMysql() stmt, _ := db.Prepare("SELECT * FROM student WHERE id = ?") var g sync.WaitGroup g.Add(3) for i := 1; i < 4; i++ { // 创建三个协程只从sql语句 go func(i int) { var ( IDint Namestring Ageint School string Sexint ) if err := stmt.QueryRow(i).Scan(&ID, &Name, &Age, &School, &Sex); err != nil { fmt.Println(err) } else { fmt.Println(ID, Name, Age, School, Sex) } g.Done() }(i) } g.Wait() }

Output:
$ go run main.go Connect Mysql Success.... 3 random1 20 school1 1 2 random 20 school1 1 1 random 20 school1 1

我们可以看到三个协程都执行成功了。
5. 事务 通过 db.Begin() 可以生成一个事务 Tx ,相信大家对对数据库的事务是有一定了解的,一次事务必须以对Commit或Rollback的调用结束。下面是 Tx 相关的方法:
// Exec执行命令,但不返回结果。例如执行insert和update。 func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) // Query执行查询并返回零到多行结果(Rows),一般执行select命令。 func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) // QueryRow执行查询并期望返回最多一行结果(Row)。 // QueryRow总是返回非nil的结果,查询失败的错误会延迟到在调用该结果的Scan方法时释放。 func (tx *Tx) QueryRow(query string, args ...interface{}) *Row // Prepare准备一个专用于该事务的状态。 // 返回的该事务专属状态操作在Tx递交会回滚后不能再使用。 // 要在该事务中使用已存在的状态,参见Tx.Stmt方法。 func (tx *Tx) Prepare(query string) (*Stmt, error) // Stmt使用已存在的状态生成一个该事务特定的状态。 func (tx *Tx) Stmt(stmt *Stmt) *Stmt // Commit递交事务。 func (tx *Tx) Commit() error // Rollback放弃并回滚事务。 func (tx *Tx) Rollback() error

举个例子:
package mainimport ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" )func main() { db, _ := ConnectMysql() tx, _ := db.Begin() // 写入数据 result, err := tx.Exec("INSERT INTO student (name,age,school,sex) VALUES (?, ?, ?, ?) ", "random_w", 18, "school2", 2) if err != nil { _ = tx.Rollback() fmt.Println(err) return } // 获取写入数据的ID id, _ := result.LastInsertId() var ( IDint Namestring Ageint School string Sexint ) // 通过ID检索数据 if err := tx.QueryRow("SELECT * FROM student WHERE id = ?", id).Scan(&ID, &Name, &Age, &School, &Sex); err != nil { _ = tx.Rollback() fmt.Println(err) return } fmt.Println(ID, Name, Age, School, Sex) _ = tx.Commit() return }

【#|Go语言标准库学习之database/sql——数据库管理的利器】Output:
$ go run main.go Connect Mysql Success.... 8 random_w 18 school2 2

    推荐阅读