Golang Package-database/sql

2017-01-10 21:00:10  吃一堑消化不良
阅读 421 次  0 条评论  收藏

一、导入库

import (
    "database/sql"
    _ "github.com/lib/pq"
)

 

二、连接DB

func main() {
    db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")
    /*db, err := sql.Open("postgres", 
          "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full")*/
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

        sql.Open的第一个参数是driver名称,第二个参数是driver连接数据库的信息。DB不是连接,并且只有当需要使用时才会创建连接,如果想立即验证连接,需要用Ping()方法,如下:

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

        sql.DB的设计就是用来作为长连接使用的。不要频繁Open, Close。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象Open。如果需要短连接,那么把DB作为参数传入function,而不要在function中Open, Close。

        在database/sql中有一个很基本的连接池,当需要连接,且连接池中没有可用连接时,新的连接就会被创建;如果长时间保持空闲连接,可能会导致db timeout。可以设置SetMaxIdleConns和SetMaxOpenConns,也就是最大空闲连接和最大连接数,分别是下面这两个函数:

db.SetMaxIdleConns(n)
db.SetMaxOpenConns(n)

 

三、查询DB

(1)一般查询Query

var name, sex string
rows, err := db.Query("select name, sex from user where id = $1 ", 1)
if err != nil {
	fmt.Println(err)
}
defer rows.Close()

for rows.Next() {
	err := rows.Scan(&name, &sex)
	if err != nil {
		fmt.Println(err)
	}
}

err = rows.Err()
if err != nil {
	fmt.Println(err)
}

fmt.Println("name:", name, "sex:", sex)

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

        (1) 检查遍历是否有error
        (2) 结果集(rows)未关闭前,底层的连接处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows.Close(),但是如果提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭。所以手动关闭非常重要。rows.Close()可以多次调用,是无害操作。

(2)单条查询QueryRow

var name string
err = db.QueryRow("select name from user where id = $1", 222).Scan(&name)
if err != nil {
    if err == sql.ErrNoRows {
        // there were no rows, but otherwise no error occurred
    } else {
        log.Fatal(err)
    }
}
fmt.Println(name)

 

四、增删改Exec

Prepared Statements and Connection
      在数据库层面,Prepared Statements是和单个数据库连接绑定的。客户端发送一个有占位符的statement到服务端,服务器返回一个statement ID,然后客户端发送ID和参数来执行statement。

       在GO中,连接不直接暴露,你不能为连接绑定statement,而是只能为DB或Tx绑定。database/sql包有自动重试等功能。当你生成一个Prepared Statement

      (1)自动在连接池中绑定到一个空闲连接
      (2)Stmt对象记住绑定了哪个连接
      (3)执行Stmt时,尝试使用该连接。如果不可用,例如连接被关闭或繁忙中,会自动re-prepare,绑定到另一个连接。
       这就导致在高并发的场景,过度使用statement可能导致statement泄漏,statement持续重复prepare和re-prepare的过程,甚至会达到服务器端statement数量上限。

有些场景不适合用statement

      (1)数据库不支持。例如Sphinx,MemSQL。他们支持MySQL wire protocol, 但不支持"binary" protocol。
       (2)statement不需要重用很多次,并且有其他方法保证安全。

stmt, err := db.Prepare("insert into user(name, sex)values($1,$2)")
if err != nil {
	fmt.Println(err)
}

rs, err := stmt.Exec("go-test", 12)
if err != nil {
	fmt.Println(err)
}
//可以获得影响行数
affect, err := rs.RowsAffected()

fmt.Println("affect ", affect ," rows")

 

五、事务

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO foo VALUES ($1)")
if err != nil {
    log.Fatal(err)
}

for i := 0; i < 10; i++ {
    _, err = stmt.Exec(i)
    if err != nil {
        log.Fatal(err)
    }
}
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}
defer stmt.Close() //runs here!

        db.Begin()开始事务,Commit() Rollback()关闭事务。Tx从连接池中取出一个连接,在关闭之前都是使用这个连接。Tx不能和DB层的BEGIN, COMMIT混合使用

 

本文来自:开源中国博客

感谢作者:吃一堑消化不良

查看原文:Golang Package-database/sql

0条评论

文章点评:

(您需要 登录 后才能评论 没有账号 ?)
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet